一道原生js题目引发的思考(鼠标停留区块计时)
我瞎逛个啥论坛,发现了一个题目,于是本着练手的心态就开始写起来了,于是各种问题接踵而至,收获不小。
题目是这样的:
Demo: mouseenter与mouseover区别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.有机会听取别人的意见改进自己是件非常好的事情,欧耶~
一道原生js题目引发的思考(鼠标停留区块计时)的更多相关文章
- 原生js中获取this与鼠标对象以及vue中默认的鼠标对象参数
1.通过原生js获取this对象 <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...
- 原生js实现在表格用鼠标框选并有反选功能
今天应同学要求,需要写一个像Excel那样框选高亮,并且实现框选区域实现反选功能.要我用原生js写,由于没什么经验翻阅了很多资料,第一次写文章希望各位指出不足!! 上来先建表 <div clas ...
- 由一段JS代码引发的思考
不知道大家在编程的时候有没有遇到过这种情况,就是在循环遍历删除一部分内容的时候,发现只能删除其中一部分,而另一部分却总也删不掉,然后觉得自己的逻辑没有问题啊,于是陷入了深深的抑郁之中…… 昨天在处理一 ...
- 一道money计算题引发的思考
网友提出一个问题如下 是小学和中学时候学到了增长折线问题,有点像数学问题,不过这个要求用编程来实现,恐怕还是有些逻辑要处理的,话不多说看代码吧 我给出的代码如下 代码清单: <?php func ...
- 再谈React.js实现原生js拖拽效果
前几天写的那个拖拽,自己留下的疑问...这次在热心博友的提示下又修正了一些小小的bug,也加了拖拽的边缘检测部分...就再聊聊拖拽吧 一.不要直接操作dom元素 react中使用了虚拟dom的概念,目 ...
- 解决一道leetcode算法题的曲折过程及引发的思考
写在前面 本题实际解题过程是 从 40秒 --> 24秒 -->1.5秒 --> 715ms --> 320ms --> 48ms --> 36ms --> ...
- React.js实现原生js拖拽效果及思考
一.起因&思路 不知不觉,已经好几天没写博客了...近来除了研究React,还做了公司官网... 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖 ...
- 原生js获取鼠标坐标方法全面讲解-zmq
原生js获取鼠标坐标方法全面讲解:clientX/Y,pageX/Y,offsetX/Y,layerX/Y,screenX/Y 一.关于js鼠标事件综合各大浏览器能获取到坐标的属性总共以下五种:eve ...
- 基于css3新属性transform及原生js实现鼠标拖动3d立方体旋转
基于css3新属性transform,实现3d立方体的旋转 通过原生JS,点击事件,鼠标按下.鼠标抬起和鼠标移动事件,实现3d立方体的拖动旋转,并将旋转角度实时的反应至界面上显示 实现原理:通过获取鼠 ...
随机推荐
- PHP 汉字转拼音类
本文转载自:http://www.epubit.com.cn/article/867 <?php function Pinyin($_String, $_Code='gb2312') { $_D ...
- RHEL7关于时间的学习笔记
当你发现时间是贼了,它早已偷光你的选择. 一,GMT.UTC.CST GMT:(Greenwich Mean Time)格林威治时间 ,太阳通过格林威治那一刻来作为计时标准. UTC:(Coordin ...
- Java 内部类摘抄
关于Java的内部类,要说的东西实在太多,这篇博文中也无法一一具体说到,所以就挑些重点的讲.关于内部类的使用,你可能会疑问,为什么我们要使用内部类?为了回答这个问题,你需要知道一些关于内部类的重点.所 ...
- 锁的封装 读写锁、lock
最近由于项目上面建议使用读写锁,而去除常见的lock锁.然后就按照需求封装了下锁.以简化锁的使用.但是开发C#的童鞋都知道lock关键字用起太方便了,但是lock关键字不支持超时处理.很无奈,为了实现 ...
- Python中的threading
Python中的threading RLock--重入锁 RLock在Python中的实现是对Lock的封装,具体在类中维护了一个重入次数的变量.一旦一个线程获得一个RLock,该线程再次要求获得该锁 ...
- [WPF]Slider控件常用方法
WPF的Slider控件继承自RangeBase类型,同继承自RangeBase的控件还有ProgressBar和ScrollBar,这类控件都是在一定数值范围内表示一个值的用途. 首先注意而Rang ...
- webrtc进阶-信令篇-之三:信令、stun、turn、ice
webRTC支持点对点通讯,但是webRTC仍然需要服务端: . 协调通讯过程中客户端之间需要交换元数据, 如一个客户端找到另一个客户端以及通知另一个客户端开始通讯. . 需要处理NAT(网 ...
- 异常处理和JDBC
1.异常: 格式:try{ 要执行的可能出现异常的语句 } catch(Exception e){ 对异常进行处理的语句 } finally{ 一定会被处理的语句 //可以不写 } 当需要 ...
- JAVA抓取URL
package com.ais.plugin.analyse.test; import com.ais.plugin.analyse.util.MD5; import java.io.*; impor ...
- 解决mac安装grunt时出现[command not found]的错误
第一步: 1先确定一下.bash_profile是否存在. 2在mac终端输入: test -e .bash_profile && echo "found" || ...