详解浏览器事件捕捉,冒泡

浏览器事件模型中的过程次要分为三个阶段:捕捉阶段、指标阶段、冒泡阶段

    <div id="parent" class="flex-center">        parent        <p id="child" class="flex-center">            child            <span id='son' class="flex-center">                son                <a href="https://www.baidu.com" id="a-baidu">点我啊</a>            </span>        </p>    </div>
    const parent = document.getElementById("parent");    const child = document.getElementById("child");    const son = document.getElementById("son");    window.addEventListener("click", function (e) {        // e.target.nodeName 指以后点击的元素, e.currentTarget.nodeName绑定监听事件的元素        console.log("window 捕捉", e.target.nodeName, e.currentTarget.nodeName);    }, true);    parent.addEventListener("click", function (e) {        // e.stopPropagation();                console.log("parent 捕捉", e.target.nodeName, e.currentTarget.nodeName);    }, true);    child.addEventListener("click", function (e) {        console.log("child 捕捉", e.target.nodeName, e.currentTarget.nodeName);    }, true);    son.addEventListener("click", function (e) {        console.log("son 捕捉", e.target.nodeName, e.currentTarget.nodeName);    }, true);    window.addEventListener("click", function (e) {        console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);    }, false);    parent.addEventListener("click", function (e) {        console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);    }, false);    child.addEventListener("click", function (e) {        console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);    }, false);    son.addEventListener("click", function (e) {        console.log("son 冒泡", e.target.nodeName, e.currentTarget.nodeName);    }, false);

第三个参数

这里要留神addEventListener的第三个参数, 如果为true,就是代表在捕捉阶段执行。如果为false,就是在冒泡阶段进行

阻止事件流传

  • e.stopPropagation()
    大家常听到的是阻止冒泡,实际上这个办法不止能阻止事件冒泡,还能阻止捕捉阶段的流传
  • stopImmediatePropagation()
    如果有多个雷同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会依照被增加的程序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 办法,则以后元素剩下的监听函数将不会被执行。

场景设计:

先有一个页面,这个页面上有许多元素, div p button
每个元素上都有本人的click事件,都不雷同;

当初来了一个新的需要,一个用户进入这个页面的时候,会有一个状态banned, window.banned.
true: 以后用户被封禁了,点击任何元素,都不执行现有click,而是提醒被封禁了
false:不作任何操作。

    window.addEventListener("click", function (e) {        if(window.banned){            e.stopPropagation();            alert('你被封禁了');            return;        }        // ...    }, true);

阻止默认行为

  • e.preventDefault()
    e.preventDefault()能够阻止事件的默认行为产生,默认行为是指:点击a标签就转跳到其余页面、拖拽一个图片到浏览器会主动关上、点击表单的提交按钮会提交表单等等,因为有的时候咱们并不心愿产生这些事件,所以须要阻止默认行为

兼容性

attachEvent——兼容:IE7、IE8; 不反对第三个参数来管制在哪个阶段产生,默认是绑定在冒泡阶段
addEventListener——兼容:firefox、chrome、IE、safari、opera;

// 兼容写法class BomEvent{    constructor(element){        this.element = element;    }    addEvent(type, handler){        if(this.element.addEventListener){            this.element.addEventListener(type, handler, false);  // IE不反对捕捉        }else if(this.element.attachEvent){            this.element.attachEvent(`on${type}`, handler)        }else{            this.element[`on${type}`] = handler;        }    }    removeEvent(){        if(this.element.removeEventListener){            this.element.removeEventListener(type, handler, false);        }else if(this.element.detachEvent){            this.element.detachEvent(`on${type}`, handler)        }else{            this.element[`on${type}`] = null;        }    }}// 阻止事件流传function StopPropagation(ev){    if(ev.stopPropagation){        ev.stopPropagation();  // W3C    }else{        ev.cancelBubble = true;  // IE    }}// 阻止默认行为function preventDefault(ev){    if(ev.preventDefault){        ev.preventDefault()    }else{        ev.returnValue = false;    }}

绑定事件的使用,以及封装一个多浏览器兼容的绑定事件函数

大家常见的一个面试题可能是ul + li,点击每个li alert对应的索引,这里就给大家来写一下看看

  • 先来给每个li绑定事件
  • 再来写一个事件委托的形式

      <ul id="ul">      <li>1</li>      <li>2</li>      <li>3</li>      <li>4</li>      <!-- ... -->  </ul>
      // const liList = document.getElementsByTagName("li");  // for(let i = 0; i<liList.length; i++){  //     liList[i].addEventListener('click', function(e){  //         alert(`内容为${e.target.innerHTML}, 索引为${i}`);  //    })  // }  const ul = document.querySelector("ul");  ul.addEventListener('click', function(e) {      const target = e.target;      if(target.tagName.toLowerCase() === 'li'){          const lists = document.querySelector("li");          const index = Array.from(lists).indexOf(target);          // Array.prototype.indexOf.call(lists, target);         alert(`内容为${target.innerHTML}, 索引为${index}`);      }  })