• 优化javascript中mouseover和mouseout事件


    之前做开发时就遇到了这个问题,表现为:

    当为某个容器绑定了 onmouseover 或者onmouseout 事件时,如果这个容器中有其它元素节点,那么鼠标在内部移动时会频繁触发 onmouseover和onmouseout 事件。这有个实例,别人的,可以参考下。

    而我想要的效果是:事件仅在鼠标进入/离开元素区域触发一次,当鼠标在元素区域内部移动的时候不会触发。

    为什么会出现这个原因呢?其实是因为事件冒泡导致的。当鼠标移上或者移出容器中的子节点时,会分别触发mouseover和mouseout事件, 紧随着dom树向上冒泡传递,直到被事件处理程序(监听器)捕获捕获或者冒泡到根节点(document或者window),也就是说事件会向它的父级对 象派发。

    知道了问题产生原因,那么解决起来是不是也很简单呢?最初我想的是取消事件冒泡,使用event.cancelBubble = true(IE)和e.stopPropagation()(其 它浏览器),但是简单测试后发现貌似没有什么效果,问题依旧,貌似冒泡停止不了,原因不明。(补充:我是测试将容器中的a链接节点取消冒泡,但是发现鼠标 移上移下还会触发事件。a节点下还有span节点。难道要将容器中所有节点都取消冒泡才行?有心人可以测试,如果真的这样,那也太恶心了,要是N多的节 点,难道都要停止冒泡下?)

     

    其实在IE下鼠标事件有个 mouseEnter 和 mouseLeave,这个就是移进和移处容器时触发一次,在内部移动则不会触发,遗憾的是只有IE支持。我们现在要做的就是“为非IE浏览器添加mouseEnter和mouseLeave支持”。

    我翻阅了百度最新开源的JS库tangram,看了里面的处理,发现它貌似是单独处理了非IE浏览器下的事件,使用一个叫 “baidu.event._eventFilter._crossElementBoundary(listener, e)”的方法修正mouseover和mouseout,然后封装了个mouseEnter和mouseLeave事件。

    /**
     * 事件仅在鼠标进入/离开元素区域触发一次,当鼠标在元素区域内部移动的时候不会触发,用于为非IE浏览器添加mouseleave/mouseenter支持。
     * 
     * @name baidu.event._eventFilter._crossElementBoundary
     * @function
     * @grammar baidu.event._eventFilter._crossElementBoundary(listener, e)
     * 
     * @param {function} listener    要触发的函数
     * @param {DOMEvent} e             DOM事件
     */
    baidu.event._eventFilter._crossElementBoundary = function(listener, e){
        /* e.relatedTarget是取得触发事件的节点,包括冒泡的事件;而e.currentTarget则是绑定事件的容器节点 */
        var related = e.relatedTarget,
            current = e.currentTarget;
        /* 这个。。。我想不通related啥时候会是undefined,没有related的话谁来触发? */
        if(typeof related == 'undefined'){
            return listener.call(current, e);
        }
        if(
           related === false || 
           // 如果current和related都是body,contains函数会返回false
           current == related ||
           // Firefox有时会把XUL元素作为relatedTarget
           // 这些元素不能访问parentNode属性
           // thanks jquery & mootools
           related.prefix == 'xul' ||
           //如果current包含related,说明没有经过current的边界
           baidu.dom.contains(current, related)//注:baidu.dom.contains是个定义的检测节点是否包含的函数,下面我会讲到
          ){
            return ;
        }
        //调用执行
        return listener.call(current, e);
    };

    百度的=方法我并不喜欢,首先它只对非IE浏览器进行了处理,当然,它又进行了封装,可以直接使用mouseEnter和mouseLeave;但是,我们做普通开发,没必要这么封装,我只是想要简单的去掉mouseover和mouseout的这个恼人特性。

    而jQuery则不是这么做的,它是直接对IE和其它所有浏览器下的mouseover和mouseout事件进行了修正。参考jQuery,我得到了我目前所有的代码。

    首先,介绍个判断节点对象是否包含的函数contains

    /* p=parentNode, c=childNode */
    function contains(p,c){  
        return p.contains ? 
               p != c && p.contains(c) :
               !!(p.compareDocumentPosition(c) & 16);  
    }

    然后就是重点的了,这里我们在IE下用到了fromElement和toElement,这两个是IE下的鼠标移上去时和移出时的节点对象。

    /* e即为事件,target即为绑定事件的节点 */
    function fixedMouse(e,target){  
            var related,
                type=e.type.toLowerCase();//这里获取事件名字
            if(type=='mouseover'){
                related=e.relatedTarget||e.fromElement
            }else if(type='mouseout'){
                related=e.relatedTarget||e.toElement
            }else return true;
            return related && related.prefix!='xul' && !contains(target,related) && related!==target;
        }

    然后我们怎么用呢?比如在绑定事件时,

    //addListener为封装的事件绑定函数
    addListener(target,'mouseover',function(e){
        e=e||window.event;
        if(fixedMouse(e, target)){
            //do something
        }
    },false);

    这样就会只在移入移出target节点时触发mouseover和mouseout了。

    当然,你也可以将上面的代码单独封装成mouseEnter和mouseLeave,这样可以以后调用时更好区别mouseover和mouseout。

    具体demo请等我新主题上线时可以给大家看看。或者看这里的评论部分的回复、引用按钮的显隐,即用到了上面讲的。

  • 相关阅读:
    2022.6.10 Vue框架学习 信1905
    Unity加强版或专业版,输入序列号提示,您的序列号无效
    简单又快速的格式化日期时间方法
    webstorm常用配置
    20192423 202120222 《网络与系统攻防技术》实验八实验报告
    嵌入式开发入门名词概念
    USB移动硬盘 显示打印设备和打印机
    TCP连接:SYN ACK RST UTG PSH FIN
    php 跑
    前端——html——html列表
  • 原文地址:https://www.cnblogs.com/baoyiluo/p/3090590.html
Copyright © 2020-2023  润新知