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

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

题目是这样的:

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

Demo: mouseenter与mouseover区别demo

跨浏览器的区块计数demo

DownLoad:https://github.com/zhangmengxue/Practice/timeCounter.html

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

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

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

->mouseleave:在位于元素上方的鼠标光标移动到这个元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发;

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

->mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的一个元素可能是位于前一个元素的外部,也可能是这个元素的子元素。

尝试给下面这段代码分别绑定mouseenter和mouseover事件:

     <ul id="test">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>

猛戳这里看demo:mouseenter与mouseover区别demo

根据例子可以看出来,虽然事件都是绑定到父元素ul上了,但是mouseover的事件对象其实却是子元素li,而mouseenter的事件对象才是真正的ul元素。

也就是说我的demo因为简单,所以这两个事件的区别在我的demo差别不明显,但是如果我的区块中有子元素的话,那么就会有问题了。

恩,于是遂改mouseenter和mouseleave,多么痛的领悟。

第一个版本的代码:

     <script type="text/javascript">
var counter = 0,
counter1 = 0,
counter2 = 0,
counter3 = 0;
var timer = null;
function timerCounter(div,counter,num){
div.addEventListener('mouseenter',function(){
timer = setInterval(count,1000);
},false);
div.addEventListener('mouseleave',function(){
clearInterval(timer);
//这里设置null是很有必要的,为了重复计数
timer = null;
},false);
var span = document.createElement('span');
div.appendChild(span);
function count(){
counter++;
span.innerHTML = 'STAY:'+counter;
console.log('区块'+num+'上停留的时间为:'+counter);
}
return counter;
}
var div = document.getElementsByTagName('div');
timerCounter(div[0],counter1,div[0].dataset['spmid']);
timerCounter(div[1],counter2,div[1].dataset['spmid']);
timerCounter(div[2],counter3,div[2].dataset['spmid']);
</script>

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

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

     <script type="text/javascript">
var startTime = 0,
totalTime = 0,
stayTime = 0,
counter1 = 0,
counter2 = 0,
counter3 = 0;
var timer = null;
function timerCounter(div,startTime,totalTime,num){
var span = document.createElement('span');
div.addEventListener('mouseenter',function(){
//换成用Date对象
startTime = new Date().getTime();
},false);
div.addEventListener('mouseleave',function(){
totalTime += new Date().getTime()-startTime;
//console.log(totalTime);
span.innerHTML = '鼠标离开后才会显示停留时长,stay:'+Math.floor(totalTime/1000);
},false);
div.appendChild(span);
}
var div = document.getElementsByClassName('mod-spm');
timerCounter(div[0],startTime,totalTime,div[0].dataset['spmid']);
timerCounter(div[1],startTime,totalTime,div[1].dataset['spmid']);
timerCounter(div[2],startTime,totalTime,div[2].dataset['spmid']);
</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()方法获取。

然后改进改进改进:

 <script type="text/javascript">
var counter = 0,
counter1 = 0,
counter2 = 0,
counter3 = 0;
var timer = null;
function timerCounter(div,counter,num){
addEvent(div,'mouseenter',function(){
timer = setInterval(count,1000);
});
addEvent(div,'mouseleave',function(){
clearInterval(timer);
//这里设置null是很有必要的,为了重复计数
timer = null;
});
var span = document.createElement('span');
div.appendChild(span);
function count(){
counter++;
span.innerHTML = 'STAY:'+counter;
console.log('区块'+num+'上停留的时间为:'+counter);
}
return counter;
} //跨浏览器的事件处理函数
var addEvent = function( obj, type, fn ) {
if (obj.addEventListener)
obj.addEventListener( type, fn, false );
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj.attachEvent( "on"+type, function() {
obj["e"+type+fn]();
} );
}
}; //解决getElementsByClassName在低版本ie中不支持的问题
var getElementsByClassName = function(searchClass,node,tag) {
if(document.getElementsByClassName){
return document.getElementsByClassName(searchClass)
}else{
node = node || document;
tag = tag || '*';
var returnElements = []
var els = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag);
var i = els.length;
searchClass = searchClass.replace(/\-/g, "\\-");
var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
while(--i >= 0){
if (pattern.test(els[i].className) ) {
returnElements.push(els[i]);
}
}
return returnElements;
}
} var div = getElementsByClassName('mod-spm');
var block1 = num(div[0]);
var block2 = num(div[1]);
var block3 = num(div[2]);
timerCounter(div[0],counter1,block1);
timerCounter(div[1],counter2,block2);
timerCounter(div[2],counter3,block3);
//解决data-*的兼容性问题
function num(div){
if(div.dataset){
return div.dataset['spmid'];
}else{
return div.getAttribute('data-spmid');
}
} </script>

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

总结:

1.知识点要分析的准确,比如说像mouseover与mouseenter这样的,这样写代码的时候才不会出现各种各样费解的问题;

2.既然意识到了自己的代码写的不太好有些乱,那么就后面努力在这方面下工夫,努力让它看起来“不错”些;

3.有机会听取别人的意见改进自己是件非常好的事情,欧耶~

上一篇:mysql查看修改字符集


下一篇:three.js阴影