基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

什么是事件冒泡 (Bubbling) 和事件捕获 (Capturing)?

知识点图片

事件冒泡 (Event Bubbling)事件捕获 (Event Capturing) 是 JavaScript 中处理 DOM 事件流(Event Flow)的两种机制。它们描述了当你在页面上触发一个事件(例如点击一个按钮)时,这个事件如何在 DOM 树的元素之间传递。

为了理解这两个概念,首先要明白 DOM 是树状结构的。当你点击一个子元素时,你实际上也点击了它的父元素、祖父元素,直到 window 对象。


1. 核心概念

事件捕获 (Capturing) —— “从上往下”

  • 方向: 从 DOM 树的根节点(Root)向目标元素(Target)传播。
  • 过程: 事件首先被最外层的元素(如 windowdocument)捕获,然后一层层向下传递,直到到达实际触发事件的目标元素。
  • 比喻: 就像公司老板下达命令,先经过经理,再经过组长,最后传达到具体的员工。

事件冒泡 (Bubbling) —— “从下往上”

  • 方向: 从目标元素(Target)向 DOM 树的根节点(Root)传播。
  • 过程: 事件先在目标元素上触发,然后像水里的气泡一样,一层层向上冒,触发父元素、祖父元素的同类型事件,直到 window
  • 比喻: 就像员工有个诉求,先告诉组长,组长告诉经理,经理再汇报给老板。
  • 注意: 这是现代浏览器处理事件的默认方式。

2. W3C 标准事件流

现代浏览器遵循 W3C 标准,将事件传播分为三个阶段:

  1. 捕获阶段 (Capturing Phase): 事件从 Window 向下传递到目标元素。
  2. 目标阶段 (Target Phase): 事件到达实际的目标元素。
  3. 冒泡阶段 (Bubbling Phase): 事件从目标元素向上回传到 Window。

3. 代码演示与控制

在 JavaScript 中,我们使用 addEventListener 来监听事件。它的第三个参数决定了监听器是在“捕获阶段”还是“冒泡阶段”触发。

javascript
element.addEventListener(event, function, useCapture);
  • useCapture (可选):
    • false (默认值): 在 冒泡阶段 触发。
    • true: 在 捕获阶段 触发。

示例场景

假设结构如下:
div (父) > button (子)

html
<div id="parent">
  <button id="child">点击我</button>
</div>
javascript
const parent = document.getElementById('parent');
const child = document.getElementById('child');

// 捕获阶段监听 (true)
parent.addEventListener('click', () => {
  console.log('父元素 - 捕获阶段');
}, true);

child.addEventListener('click', () => {
  console.log('子元素 - 捕获阶段');
}, true);

// 冒泡阶段监听 (false 或省略)
parent.addEventListener('click', () => {
  console.log('父元素 - 冒泡阶段');
}, false);

child.addEventListener('click', () => {
  console.log('子元素 - 冒泡阶段');
}, false);

当你点击按钮时,控制台输出顺序如下:

  1. 父元素 - 捕获阶段 (事件从顶层下来,先遇到父元素)
  2. 子元素 - 捕获阶段 (事件到达目标)
  3. 子元素 - 冒泡阶段 (事件开始往上冒)
  4. 父元素 - 冒泡阶段 (事件冒泡到父元素)

4. 阻止传播 (stopPropagation)

有时候我们不希望事件继续传播(例如:点击弹窗内的按钮,不希望触发弹窗背景的关闭事件)。我们可以使用 event.stopPropagation()

  • 如果在 捕获 阶段调用:事件将停止向下传递,目标元素可能接收不到事件。
  • 如果在 冒泡 阶段调用:事件触发当前元素后,不会再向上传递给父元素。
javascript
child.addEventListener('click', (event) => {
  event.stopPropagation(); // 阻止冒泡
  console.log('子元素被点击,父元素不会收到通知');
});

5. 实际应用:事件委托 (Event Delegation)

事件委托是利用 事件冒泡 机制的最常见应用。

场景: 你有一个列表 <ul>,里面有 1000 个 <li>
笨办法: 给每个 <li> 都绑定一个监听器(消耗内存,性能差)。
好办法(事件委托): 给父元素 <ul> 绑定一个监听器。

当用户点击 <li> 时,事件会冒泡到 <ul>。我们在 <ul> 的监听器里通过 event.target 就能知道具体点击了哪个 <li>

javascript
document.getElementById('myList').addEventListener('click', function(e) {
  // e.target 是实际被点击的元素
  if (e.target && e.target.nodeName === 'LI') {
    console.log('List item clicked!', e.target.textContent);
  }
});

总结

特性 事件捕获 (Capturing) 事件冒泡 (Bubbling)
方向 \rightarrow 内 (Top-Down) \rightarrow 外 (Bottom-Up)
默认状态 需要显式开启 (true) addEventListener 的默认行为
主要用途 较少使用,用于在事件到达目标前拦截 非常常用,用于事件委托
00:00
00:00