事件触发顺序

DOM 事件的捕获、目标、冒泡三个阶段

问题

DOM 事件触发时,经历哪些阶段?事件处理函数的执行顺序是什么?

解答

事件传播的三个阶段

  1. 捕获阶段:从 window 向下传播到目标元素
  2. 目标阶段:到达目标元素
  3. 冒泡阶段:从目标元素向上传播到 window
         window

           ▼  捕获阶段
        document


         <html>


         <body>


         <div>


        <button>  ← 目标阶段

           ▼  冒泡阶段
         (向上返回)

代码示例

<!DOCTYPE html>
<html>
<body>
  <div id="outer">
    <button id="inner">点击我</button>
  </div>

  <script>
    const outer = document.getElementById('outer');
    const inner = document.getElementById('inner');

    // 捕获阶段触发(第三个参数为 true)
    outer.addEventListener('click', () => {
      console.log('outer 捕获');
    }, true);

    inner.addEventListener('click', () => {
      console.log('inner 捕获');
    }, true);

    // 冒泡阶段触发(第三个参数为 false 或省略)
    outer.addEventListener('click', () => {
      console.log('outer 冒泡');
    }, false);

    inner.addEventListener('click', () => {
      console.log('inner 冒泡');
    }, false);

    // 点击 button 输出顺序:
    // outer 捕获
    // inner 捕获
    // inner 冒泡
    // outer 冒泡
  </script>
</body>
</html>

目标阶段的特殊情况

在目标元素上,事件处理函数按注册顺序执行,不区分捕获和冒泡:

const btn = document.getElementById('btn');

// 先注册冒泡
btn.addEventListener('click', () => {
  console.log('冒泡');
}, false);

// 后注册捕获
btn.addEventListener('click', () => {
  console.log('捕获');
}, true);

// 点击 btn 输出:
// 冒泡
// 捕获
// (按注册顺序,而非先捕获后冒泡)

阻止传播

element.addEventListener('click', (e) => {
  // 阻止事件继续传播(捕获或冒泡)
  e.stopPropagation();
  
  // 阻止传播,同时阻止当前元素上其他同类型事件处理函数执行
  e.stopImmediatePropagation();
});

事件委托应用

利用冒泡机制,在父元素上统一处理子元素事件:

document.getElementById('list').addEventListener('click', (e) => {
  // 判断实际点击的元素
  if (e.target.tagName === 'LI') {
    console.log('点击了:', e.target.textContent);
  }
});

关键点

  • 事件传播顺序:捕获 → 目标 → 冒泡
  • addEventListener 第三个参数:true 捕获阶段触发,false(默认)冒泡阶段触发
  • 目标元素上的事件按注册顺序执行,不区分捕获冒泡
  • stopPropagation() 阻止传播,stopImmediatePropagation() 还能阻止同元素其他处理函数
  • 事件委托利用冒泡机制,减少事件绑定数量