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

事件冒泡和事件捕获

作者: 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