美文网首页
事件冒泡和事件捕获

事件冒泡和事件捕获

作者: my木子 | 来源:发表于2021-04-10 13:31 被阅读0次

    事件

    • 事件是文档和浏览器窗口中发生的特定的交互瞬间
    • 事件可能是用户在某些内容上的点击,鼠标经过某个特定元素或按下键盘上的某些按键,事件还可能是web浏览器中发生的事情,比如说某个web页面加载完成,或者是用户滚动窗口或改变窗口大小。

    事件流

    • 事件流描述的是从页面中接受事件的顺序,但有意思的是,微软(IE)和网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)。
    • 事件冒泡流:div ==> body ==> html ==> document
    • 事件捕获流:document ==> html ==> body ==> div

    DOM事件流

    image.png
    // html
            <body>
                <button>
                    <span>点击</span>
                </button>
            </body>
    // js
            var body =document.querySelector('body');
            var button =document.querySelector('button');
            var span =document.querySelector('span');
    
            // DOM 0级
            // body.onclick=function(){
            //     console.log('body');
            // }
            // button.onclick=function(){
            //     console.log('button');
            // }
            // span.onclick=function(){
            //     console.log('span');
            // }
    
            // DOM 2级
            /**
                element.addEventListener(event,function,useCapture);
                第一个参数是事件的类型(如“click”或“mousedown”)
                第二个参数是事件触发后调用的函数。
                第三个参数是个布尔值用于描述事件是冒泡(false)还是捕获(true),该参数是可选的。
            */
            body.addEventListener('click',function(){
                console.log('body');
            },true);
            button.addEventListener('click',function(){
                console.log('button');
            },true);
            span.addEventListener('click',function(){
                console.log('span');
            },true);
    
            // body.addEventListener('click',function(){
            //     console.log('body');
            // },false);
            // button.addEventListener('click',function(){
            //     console.log('button');
            // },false);
            // span.addEventListener('click',function(){
            //     console.log('span');
            // },false);
    

    阻止冒泡

        // 阻止事件冒泡
        function stopBubble(e) {
            if (e && e.stopPropagation) {
                e.stopPropagation(); // W3C
            } else {
                window.event.cancelBubble = true; // IE
            }
        }
    
        // 阻止事件默认行为
        return false;
        event.preventDefault();
    

    事件委托

    • 不在事件的发生地(直接 dom)上设置监听函数,而是在其父 元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判 断事件发生元素 DOM 的类型,来做出不同的响应
    • 好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发 机制

    移动端300ms点击延迟

    • 移动端点击有 300ms 的延迟是因为移动端会有双击缩放的这个操作,因此浏览器在 click 之后要等待 300ms,看用户有没有下一次点击,来判断这次操作是不是双击

    • 解决方案

    1. 通过 meta 标签禁用网页的缩放
    <meta name="viewport" content="user-scalable=no">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1">
    
    1. 调用一些 js 库,比如 FastClick(推荐),原理:在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即出发模拟一个 click 事件,并把浏览器在300ms之后真正的 click事件 阻止掉。
    2. CSS touch-action 属性,直接将所有的其他手势事件全都禁用掉,便能达到消除延迟的目的,但是这个属性在很多浏览器中都存在不兼容的问题,而且当设置完这个属性之后,页面的滚动也会随之被禁用,慎用。

    点击穿透

    • 使用 touchstart 代替 click 事件 引发的问题

      1. touchstart 是手指触摸屏幕就触发,有时候用户只是想滑动屏幕,却触发了touchstart事件
      2. 使用 touchstart事件 在某些场景下可能会出现点击穿透的现象。
    • 点击穿透
      click 延时问题还可能引起点击穿透的问题,就是如果我们在一个元素上注册了 touchStart 的监听事件,这个事件会将这个元素隐藏掉,我们发现当这个元素隐藏后,触发了这个元素下的一个元素的点击事件,这就是点击穿透

    • 移动端浏览器事件执行的顺序是 touchstart > touchend > click。而click事件有 300ms 的延迟,当touchstart 事件把 B 元素隐藏之后,隔了 300ms,浏览器触发了click 事件,但是此时B元素不见了,所以该事件被派发到了 A 元素身上

    通用事件侦听器函数

    const EventUtils = {
      // 视能力分别使用dom0||dom2||IE方式 来绑定事件
      // 添加事件
      addEvent: function(element, type, handler) {
        if (element.addEventListener) {
          element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
          element.attachEvent("on" + type, handler);
        } else {
          element["on" + type] = handler;
        }
      },
    
      // 移除事件
      removeEvent: function(element, type, handler) {
        if (element.removeEventListener) {
          element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
          element.detachEvent("on" + type, handler);
        } else {
          element["on" + type] = null;
        }
      },
    
      // 获取事件目标
      getTarget: function(event) {
        return event.target || event.srcElement;
      },
    
      // 获取 event 对象的引用,取到事件的所有信息,确保随时能使用 event
      getEvent: function(event) {
        return event || window.event;
      },
    
      // 阻止事件(主要是事件冒泡,因为 IE 不支持事件捕获)
      stopPropagation: function(event) {
        if (event.stopPropagation) {
          event.stopPropagation();
        } else {
          event.cancelBubble = true;
        }
      },
    
      // 取消事件的默认行为
      preventDefault: function(event) {
        if (event.preventDefault) {
          event.preventDefault();
        } else {
          event.returnValue = false;
        }
      }
    };
    

    相关文章

      网友评论

          本文标题:事件冒泡和事件捕获

          本文链接:https://www.haomeiwen.com/subject/iermkltx.html