首页 > 代码库 > 一道原生js题目引发的思考(鼠标停留区块计时)

一道原生js题目引发的思考(鼠标停留区块计时)

我瞎逛个啥论坛,发现了一个题目,于是本着练手的心态就开始写起来了,于是各种问题接踵而至,收获不小。

题目是这样的:

刚看上去,没什么特别,心里想了,我就用mouseover和mouseout事件,然后绑个定时器不就行了嘛~.......于是还没开始写呢,就被问到了,那mouseover和mouseenter这两个事件有什么区别的?为什么不用mouseenter呢?

然后我就仔细想了下mouseover和mouseenter之间的区别,下面是书上列出的定义:

 

->mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发,这个事件不冒泡。而且在光标移到它后代元素上时不会触发;

->mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。

 

mouseleave与mouseout与上面enter和over大致相同的,我觉得说白了,就是冒泡与不冒泡的区别,下面这儿有个例子,看起来就特别明显啦:

 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>mouseover与mouseenter的区别demo</title> 6 <style type="text/css"> 7    8 </style> 9 </head>10 <body>11     <span>mouseenter的效果:</span>12     <ul id="testEnter">13       <li>item 1</li>14       <li>item 2</li>15       <li>item 3</li>16     </ul> 17     <span>mouseover的效果:</span>18     <ul id="testOver">19       <li>item 1</li>20       <li>item 2</li>21       <li>item 3</li>22     </ul>     23     <script>24         25         var test = document.getElementById("test");26 27         28         testEnter.addEventListener("mouseenter", function( event ) {   29         30           event.target.style.color = "red";31 32           setTimeout(function() {33             event.target.style.color = "";34           }, 500);35 36         }, false);37         38         testOver.addEventListener("mouseover", function( event ) {   39          40           event.target.style.color = "orange";41 42           setTimeout(function() {43             event.target.style.color = "";44           }, 500);45 46         }, false);47          48     </script>49 </body>  50 </html>

 

更明显的观察区别,猛戳这里:mouseenter与mouseover区别demo

 

恩,于是根据自己的题目,我就选择了mouseover和mouseout,其实demo比较简单,这里看似并无大碍;

于是我的第一个版本出炉啦~~:

 

56     <script type="text/javascript">57          var counter = 0,58              counter1 = 0,59              counter2 = 0,60              counter3 = 0;61          var timer = null;62          function timerCounter(div,counter,num){63              div.addEventListener(mouseover,function(){64                 timer = setInterval(count,1000);             65              },false); 66             div.addEventListener(mouseout,function(){67              clearInterval(timer);68              //这里设置null是很有必要的,为了重复计数69              timer = null;70              },false);71             var span = document.createElement(span);72             div.appendChild(span);73             function count(){74                 counter++;75                 span.innerHTML = STAY:+counter;76                 console.log(区块+num+上停留的时间为:+counter);77             } 78             return counter;79          }80           var div = document.getElementsByTagName(div);81           timerCounter(div[0],counter1,div[0].dataset[spmid]);82           timerCounter(div[1],counter2,div[1].dataset[spmid]);83           timerCounter(div[2],counter3,div[2].dataset[spmid]);        84     </script>

 

看它的运行结果,猛戳这里:区块停留计数器第一个版本

一直在chrome下测试, 运行成功了,暂且高兴会儿,想个别的办法,好像用Date对象也可以实现,于是没费什么事儿,在我的chrome里,他也可以计数了,唯一不足的就是他要鼠标离开后才显示秒数,不过我没计较,因为潜意识里面意识到了我的代码很水,但还是先把它实现放上来看看:

 

56     <script type="text/javascript">57          var startTime = 0,58              totalTime = 0,59              stayTime = 0,60              counter1 = 0,61              counter2 = 0,62              counter3 = 0;63          var timer = null;64          function timerCounter(div,startTime,totalTime,num){65              var span = document.createElement(span);66              div.addEventListener(mouseover,function(){
//换成用Date对象
67 startTime = new Date().getTime(); 68 },false); 69 div.addEventListener(mouseout,function(){70 totalTime += new Date().getTime()-startTime;71 //console.log(totalTime);72 span.innerHTML = 鼠标离开后才会显示停留时长,stay:+Math.floor(totalTime/1000);73 },false);74 div.appendChild(span);75 }76 var div = document.getElementsByClassName(mod-spm);77 timerCounter(div[0],startTime,totalTime,div[0].dataset[spmid]);78 timerCounter(div[1],startTime,totalTime,div[1].dataset[spmid]);79 timerCounter(div[2],startTime,totalTime,div[2].dataset[spmid]); 80 </script>

 

看它的运行结果,猛戳:区块停留计数器第二个版本

哎,貌似也可以接受,但是当我看到别人的代码的时候,心里可就不这样想了。我就是觉得自己写完了嘛,然后就去百度下看看还有没有别的方法,看别人有没有更好的实现,于是我看到了下面这段代码:

 

1     function ShowStayTime(obj) {2         this.obj = obj;3         this.totalTime = 0;4         this.enterTime = null;5         this.divTime= document.createElement(‘div‘);6     }7     ShowStayTime.prototype = {8         constructor:ShowStayTime,9         init: function() {10             this.showStayTime();11             this.obj.appendChild(this.divTime);12             this.beginTime();13             this.leaveTime();14         },15         showStayTime: function() {16             var message = "";17             message = "停留时间" + this.totalTime + "ms";18             this.divTime.innerText = message;19         },20         beginTime: function() {21             this.obj.addEventListener("mouseenter",function() {22                 this.enterTime = new Date();23             })24         },25         leaveTime: function() {26             var temp = this;27             this.obj.addEventListener("mouseleave",function() {28                 temp.totalTime += new Date().getTime() - this.enterTime.getTime();29                 temp.showStayTime();30             })31         }32     }33     var divs = document.getElementsByClassName(‘mod-spm‘);34         var show1 = new ShowStayTime(divs[0]);35         var show2 = new ShowStayTime(divs[1]);36         var show3 = new ShowStayTime(divs[2]);37         show1.init();38         show2.init();39         show3.init();

 

用的也是Date方法,可是人家写的代码比我的 有思想多了。值得我学习呀~~于是我也更意识到了,真的还有很长的路要走,基础不能忘,设计模式这种高端的东西也要搞起来,代码也不能总那么屎下去吧,小胡子跟我说,厉害的人写代码的境界,能用原生的js实现任何他能想到的结果...

于是我又开始看我的代码,写的封装啊什么的很好的那种,我还没有那个能力,那我的代码,我还能为你做些什么呢?然后我就开始打开浏览器各种试,到IE的时候,我就傻了,看吧,都不起作用了~~这会想不改进也不行了,那就做些力所能及的吧~~~

 

那我第一个版本的实现,问题有哪些呢?实际上:

 

1. 细心的人应该早就注意到了,IE并不支持getElementsByClassName()这个函数;

2. 事件绑定没有注意跨浏览器的问题;

3. data-*的兼容性问题!!

 

这有个data-*的兼容性情况:

 

  • Internet Explorer 11+
  • Chrome 8+
  • Firefox 6.0+
  • Opera 11.10+
  • Safari 6+

对于第一个问题:本来的做法就可以是,先使用getElementsByTagName("*")取出文档中所有元素,然后进行遍历,使用正则表达式找出匹配的元素放入一个数组返回。

可是,看了正美大神的下面这篇文章: document.getElementsByClassName()的理想实现  我觉得就想用这个方法了。而且心里无比崇拜。

第二个问题:事件绑定的跨浏览器问题,IE低版本根本不支持getElementsByClassName这个方法,哎,不能总自己小打小闹呀,养成好的习惯吧,快快改进。然后又看到正美大神的这篇文章:javascript处理时间的一些兼容写法 总结的很全面的。

第三个问题:由于data-*也存在兼容性问题,所以对于IE的低版本只能用getAttribute()方法获取。

 

然后改进改进改进:

 

 

  1 <!DOCTYPE html>  2 <html lang="zh">  3 <head>  4 <meta charset=utf-8>  5 <title>timeCounter</title>  6 </head>  7   8 <style>  9   .mod-spm[data-spmid=‘123‘]{ 10      width: 100px; 11      height: 100px; 12      margin-right: auto; 13      margin-left: auto; 14      margin-bottom: 20px; 15      background-color: #fff; 16      border: 2px #000 solid; 17   } 18   .mod-spm[data-spmid=‘456‘]{ 19      width: 100px; 20      height: 100px; 21      margin-right: auto; 22      margin-left: auto; 23      margin-bottom: 20px; 24      background-color: #fff; 25      border: 2px #000 solid; 26   } 27   .mod-spm[data-spmid=‘789‘]{ 28      width: 100px; 29      height: 100px; 30      margin-right: auto; 31      margin-left: auto; 32      margin-bottom: 20px; 33      background-color: #fff; 34      border: 2px #000 solid; 35   } 36 </style> 37 <body> 38      <div class="mod-spm" data-spmid="123"> 39        <div class="child_a"></div> 40        <div class="child_b"></div> 41        <div class="child_c"></div> 42        <div class="child_d"></div> 43      </div> 44      <div class="mod-spm" data-spmid="456"> 45        <div class="child_a"></div> 46        <div class="child_b"></div> 47        <div class="child_c"></div> 48        <div class="child_d"></div> 49      </div> 50      <div class="mod-spm" data-spmid="789"> 51        <div class="child_a"></div> 52        <div class="child_b"></div> 53        <div class="child_c"></div> 54        <div class="child_d"></div> 55      </div> 56     <script type="text/javascript"> 57          var counter = 0, 58              counter1 = 0, 59              counter2 = 0, 60              counter3 = 0; 61          var timer = null; 62          function timerCounter(div,counter,num){ 63              addEvent(div,mouseover,function(){ 64                 timer = setInterval(count,1000);              65              });  66              addEvent(div,mouseout,function(){ 67              clearInterval(timer); 68              //这里设置null是很有必要的,为了重复计数 69              timer = null; 70              }); 71             var span = document.createElement(span); 72             div.appendChild(span); 73             function count(){ 74                 counter++; 75                 span.innerHTML = STAY:+counter; 76                 console.log(区块+num+上停留的时间为:+counter); 77             }  78             return counter; 79          } 80  81          //跨浏览器的事件处理函数 82          var addEvent = function( obj, type, fn ) { 83                 if (obj.addEventListener) 84                     obj.addEventListener( type, fn, false ); 85                 else if (obj.attachEvent) { 86                     obj["e"+type+fn] = fn; 87                     obj.attachEvent( "on"+type, function() { 88                         obj["e"+type+fn](); 89                     } ); 90                 } 91             }; 92             93             //解决getElementsByClassName在低版本ie中不支持的问题 94             var getElementsByClassName = function(searchClass,node,tag) { 95                     if(document.getElementsByClassName){ 96                         return  document.getElementsByClassName(searchClass) 97                     }else{     98                         node = node || document; 99                         tag = tag || *;100                         var returnElements = []101                         var els =  (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag);102                         var i = els.length;103                         searchClass = searchClass.replace(/\-/g, "\\-");104                         var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");105                         while(--i >= 0){106                             if (pattern.test(els[i].className) ) {107                                 returnElements.push(els[i]);108                             }109                         }110                         return returnElements;111                     }112                 }113 114           var div = getElementsByClassName(mod-spm);115           var block1 = num(div[0]);116           var block2 = num(div[1]);117           var block3 = num(div[2]);118           timerCounter(div[0],counter1,block1);119           timerCounter(div[1],counter2,block2);120           timerCounter(div[2],counter3,block3); 121           //解决data-*的兼容性问题122           function num(div){123              if(div.dataset){124                 return div.dataset[spmid];125              }else{126                 return div.getAttribute(data-spmid);127              }128            }   129 130     </script>131 </body>  132 </html>

 

这会换浏览器也不会出问题啦,各个浏览器下猛戳:跨浏览器的区块停留计时

需要源代码的同学自己去我的github上下载吧,不过记得先star我喔,不star不给下哈哈,star太少不开心。

 

一道原生js题目引发的思考(鼠标停留区块计时)