zoukankan      html  css  js  c++  java
  • 一道原生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.有机会听取别人的意见改进自己是件非常好的事情,欧耶~

  • 相关阅读:
    使用CustomValidate自定义验证控件
    C#中金额的大小写转换
    Andriod出错之Unable to build: the file dx.jar was not loaded from the SDK folder!
    VC 编写的打字练习
    机房工作笔记Ping只有单向通
    web服务协同学习笔记(1)
    Dll 学习3 将MDI子窗口封装在DLL中
    机房工作学习文件共享
    Andriod出错之Failed to find an AVD compatible with target 'Android 2.2'
    Andriod出错之wrapper was not properly loaded first
  • 原文地址:https://www.cnblogs.com/skylar/p/4074433.html
Copyright © 2011-2022 走看看