一文搞懂事件冒泡与事件捕获

5 阅读1分钟

你以为事件只是绑定了就完事?其实它背后还有“来龙去脉”!


🔍 什么是事件传播?

当你在网页中点击一个按钮时,这个“事件”并不会直接只作用于这个按钮,而是沿着 DOM 树进行传播。这个传播过程有三个阶段:

📶 事件传播的三个阶段

  1. 捕获阶段(Capturing Phase):事件从 window 一路向下传递到目标元素。

  2. 目标阶段(Target Phase):事件到达真正被点击的目标元素。

  3. 冒泡阶段(Bubbling Phase):事件从目标元素向上传递到 window


🔁 示例结构

<body>
  <div id="outer">
    <div id="inner">
      <button id="btn">Click me</button>
    </div>
  </div>
</body>

点击 #btn 后的事件传播顺序如下:

windowdocumenthtmlbody#outer#inner#btn      ← 目标阶段
#inner#outerbodyhtmldocumentwindow

🧪 示例代码:冒泡与捕获监听对比

// 捕获阶段
outer.addEventListener('click', () => {
  console.log('outer capture');
}, true);

// 冒泡阶段
outer.addEventListener('click', () => {
  console.log('outer bubble');
});

输出顺序:

outer capture
outer bubble

🧠 关键点记忆

比较项捕获(Capturing)冒泡(Bubbling)
方向从外到内(顶层 → 目标)从内到外(目标 → 顶层)
是否默认启用❌ 否(需设置为第三个参数 true✅ 是
使用场景特定顺序控制,如拦截早期事件最常见,用于事件委托
是否可中断否(不能中断捕获本身)event.stopPropagation()
常配合window、document 捕获事件委托、组件冒泡

🧯 如何中断传播?

// 阻止继续冒泡
event.stopPropagation();

// 阻止默认行为(如 <a> 跳转)
event.preventDefault();

🎯 实战应用:事件委托

document.getElementById('list').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    console.log('点击了某个列表项');
  }
});
  • 使用冒泡机制,不必给每个子元素绑定事件

  • 适用于动态添加元素的情况


🧩 总结一句话:

事件是从外往内捕获,从内往外冒泡的。默认是冒泡监听,要捕获得手动指定。