• 一道原生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事件:

    12     <ul id="test">
    13       <li>item 1</li>
    14       <li>item 2</li>
    15       <li>item 3</li>
    16     </ul> 

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

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

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

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

    第一个版本的代码:

    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('mouseenter',function(){
    64                 timer = setInterval(count,1000);             
    65              },false); 
    66             div.addEventListener('mouseleave',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('mouseenter',function(){
    //换成用Date对象
    67 startTime = new Date().getTime(); 68 },false); 69 div.addEventListener('mouseleave',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 <script type="text/javascript">
     2          var counter = 0,
     3              counter1 = 0,
     4              counter2 = 0,
     5              counter3 = 0;
     6          var timer = null;
     7          function timerCounter(div,counter,num){
     8              addEvent(div,'mouseenter',function(){
     9                 timer = setInterval(count,1000);             
    10              }); 
    11              addEvent(div,'mouseleave',function(){
    12              clearInterval(timer);
    13              //这里设置null是很有必要的,为了重复计数
    14              timer = null;
    15              });
    16             var span = document.createElement('span');
    17             div.appendChild(span);
    18             function count(){
    19                 counter++;
    20                 span.innerHTML = 'STAY:'+counter;
    21                 console.log('区块'+num+'上停留的时间为:'+counter);
    22             } 
    23             return counter;
    24          }
    25 
    26          //跨浏览器的事件处理函数
    27          var addEvent = function( obj, type, fn ) {
    28                 if (obj.addEventListener)
    29                     obj.addEventListener( type, fn, false );
    30                 else if (obj.attachEvent) {
    31                     obj["e"+type+fn] = fn;
    32                     obj.attachEvent( "on"+type, function() {
    33                         obj["e"+type+fn]();
    34                     } );
    35                 }
    36             };
    37            
    38             //解决getElementsByClassName在低版本ie中不支持的问题
    39             var getElementsByClassName = function(searchClass,node,tag) {
    40                     if(document.getElementsByClassName){
    41                         return  document.getElementsByClassName(searchClass)
    42                     }else{    
    43                         node = node || document;
    44                         tag = tag || '*';
    45                         var returnElements = []
    46                         var els =  (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag);
    47                         var i = els.length;
    48                         searchClass = searchClass.replace(/-/g, "\-");
    49                         var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)");
    50                         while(--i >= 0){
    51                             if (pattern.test(els[i].className) ) {
    52                                 returnElements.push(els[i]);
    53                             }
    54                         }
    55                         return returnElements;
    56                     }
    57                 }
    58 
    59           var div = getElementsByClassName('mod-spm');
    60           var block1 = num(div[0]);
    61           var block2 = num(div[1]);
    62           var block3 = num(div[2]);
    63           timerCounter(div[0],counter1,block1);
    64           timerCounter(div[1],counter2,block2);
    65           timerCounter(div[2],counter3,block3); 
    66           //解决data-*的兼容性问题
    67           function num(div){
    68              if(div.dataset){
    69                 return div.dataset['spmid'];
    70              }else{
    71                 return div.getAttribute('data-spmid');
    72              }
    73            }   
    74 
    75     </script>
    View Code

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

    总结:

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

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

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

  • 相关阅读:
    eclipse文件编辑器相关概念
    Oracle ROLLUP和CUBE 用法
    eclipse插件开发常用的选择器
    oracle多cpu并行查询
    MYECLISE6.5下CDT安装
    oracle SAMPLE 语法应用
    常用的gcc命令
    vbscript获取本机的ip地址和mac地址
    什么是Single Sign On
    eclipse插件开发中全局对象的获取
  • 原文地址:https://www.cnblogs.com/skylar/p/4074433.html
Copyright © 2020-2023  润新知