本文参考自《JavaScript高级程序设计(第3版)》13.1节事件流。

事件模型

事件捕获

  • 如果你点击了页面中的
    元素,那么这个点击事件会按照如下顺序传播。
  • document ==> html ==> body ==> div
  • 即,事件会先通知给祖宗,最后通知给儿子

image.png

事件冒泡

  • 如果你单击了页面中的
    元素,那么这个点击事件会按照如下顺序传播。
  • div ==> body ==> html ==> document
  • 即,事件会先通知给儿子,最后通知给祖宗

image.png

事件机制

DOM事件机制(处理程序)

  • 使用addEventListener()来指定事件处理程序,它接收三个参数;
  • addEventListener(监听事件类型, 事件处理函数, 布尔值)
  • 其中的布尔值指明了在什么阶段调用事件处理程序
    • 默认为false,表示在冒泡阶段调用事件处理程序;
    • 如果设为true,表示在捕获阶段调用事件处理程序;

image.png

  1. /* 举个例子 🌰 */
  2. btn.addEventListener("click", () => {
  3. alert("点击了");
  4. }, false);

举个例子 🌰

  • 源代码如下(也可以在JSBin查看)
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>JS Bin</title>
    6. </head>
    7. <body>
    8. <div id="a">
    9. <div id="b">
    10. <div id="c"></div>
    11. </div>
    12. </div>
    13. </body>
    14. </html>
    1. #a{
    2. width: 300px;
    3. height: 300px;
    4. background: tomato;
    5. }
    6. #b{
    7. width: 200px;
    8. height: 200px;
    9. background: orange;
    10. }
    11. #c{
    12. width: 100px;
    13. height: 100px;
    14. background: yellow;
    15. }
    ```javascript let a = document.getElementById(“a”); let b = document.getElementById(“b”); let c = document.getElementById(“c”);

c.addEventListener(“click”, event => { console.log(“c1”); }, false);

c.addEventListener(“click”, event => { console.log(“c2”); }, true);

b.addEventListener(“click”, event => { console.log(“b”); }, true);

a.addEventListener(“click”, event => { console.log(“a1”); }, true);

a.addEventListener(“click”, event => { console.log(“a2”) }, false);

a.addEventListener(“click”, event => { console.log(“a3”); // 阻止冒泡事件和捕获事件 // 注释前后控制台输出不同 event.stopImmediatePropagation(); }, true);

a.addEventListener(“click”, event => { console.log(“a4”); }, true); ```

  • 我们创建了三个盒子,并分别给他们加上一些点击事件。

image.png
【问】问题一:点击b或c,会输出什么?
【答】a1a3

  • stopImmediatePropagation 包含了 stopPropagation 的功能,即阻止事件传播(捕获或冒泡),但同时也阻止该元素上后来绑定的事件处理程序被调用,所以不输出 a4。
  • 因为事件捕获被拦截了,自然不会触发 b、c 上的事件,所以不输出 b、c1、c2,冒泡更谈不上了,所以不输出 a2。

【问】问题二:点击a,会输出什么?
【答】a1a2a3

  • 比较容易出错的答案是a1a3a2,虽然这三个事件处理程序注册时指定了truefalse,但现在点击的是a这个盒子本身,它处于事件流的目标(target)阶段,不是冒泡阶段、也不是捕获阶段,事件处理程序被调用的顺序是注册的顺序

【问】如果在a3中取消阻止冒泡事件和捕获事件,点击c,会输出什么?
【答】a1a3a4bc1c2a2

  • 不解释了,直接看图吧

dom2event.png

【参考资料】 【MDN】EventTarget.addEventListener() 【掘金】你真的理解事件冒泡和事件捕获吗? - CodeFei 【掘金】JS中的事件委托或事件代理详解 - 慕容初晨 【知乎】JavaScript事件委托详解 - 技术杂谈 【简书】JavaScript阻止事件冒泡 - 一点红3340 JS阻止冒泡和取消默认事件(默认行为)