首页 > 代码库 > 一道原生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题目引发的思考(鼠标停留区块计时)