Skip to content

目录

1. 核心心智模型

当你在网页上点击一个元素时,这个"点击事件"并不仅仅发生在该元素上。它经历了一个完整的旅程,这个旅程被称为 DOM 事件流。

想象一下一个三层嵌套的盒子:div (外层) > p (中层) > span (内层)。当你点击最里面的 span 时:

  1. 捕获阶段: 浏览器会从最外层的祖先 window 开始,像一个"指挥官"下达命令一样,逐级向下"探查",经过 document、<html><body>、div、p,最终到达你点击的目标 span。这个从外到内的过程就是捕获阶段。
  2. 目标阶段: 事件到达了它的最终目的地——你实际点击的 span 元素。事件在这里被触发。
  3. 冒泡阶段: 事件完成任务后,开始"汇报工作"。它会从 span 开始,逐级向上"冒泡",经过 p、div,一直到 window。这个从内到外的过程就是冒泡阶段。

一句话总结:事件先从外到内"潜入"(捕获),到达目标后,再从内到外"浮出"(冒泡)。

默认情况下,我们用 addEventListener 添加的监听器只在 冒泡阶段 执行。

2. 示例:在控制台直观展示事件流

让我们用代码来直观地看到这个完整的路径。

HTML 结构:

html
<div class="grandparent">
  Grandparent
  <div class="parent">
    Parent
    <div class="child">Child</div>
  </div>
</div>

CSS (仅为视觉效果):

css
div {
  padding: 30px;
  border: 2px solid #666;
  text-align: center;
  font-family: sans-serif;
}
.grandparent { background-color: #fdd; }
.parent { background-color: #dfd; }
.child { background-color: #ddf; }

JavaScript 实现:

我们将为每一个 div 都添加两个监听器:

  • 一个在捕获阶段触发 ({ capture: true })
  • 一个在冒泡阶段触发 (默认)
javascript
const grandparent = document.querySelector('.grandparent');
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');

// --- 捕获阶段监听器 ---
grandparent.addEventListener('click', () => {
  console.log('Grandparent - 捕获阶段');
}, { capture: true });

parent.addEventListener('click', () => {
  console.log('Parent - 捕获阶段');
}, { capture: true });

child.addEventListener('click', () => {
  console.log('Child - 捕获阶段 (也会在目标阶段触发)');
}, { capture: true });

// --- 冒泡阶段监听器 ---
grandparent.addEventListener('click', () => {
  console.log('Grandparent - 冒泡阶段');
});

parent.addEventListener('click', () => {
  console.log('Parent - 冒泡阶段');
});

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

运行与分析:

现在,打开浏览器的开发者工具,点击最内层的蓝色 Child 区域。你会在控制台看到以下输出:

// ↓ 从外到内(捕获阶段)
Grandparent - 捕获阶段
Parent - 捕获阶段
Child - 捕获阶段 (也会在目标阶段触发)

// ↓ 从内到外(冒泡阶段)
Child - 冒泡阶段
Parent - 冒泡阶段
Grandparent - 冒泡阶段

结果分析:

  1. 事件开始下沉: 点击发生后,事件流从 grandparent 开始,触发了它的捕获监听器。
  2. 继续下沉: 事件流到达 parent,触发了它的捕获监听器。
  3. 到达目标: 事件流到达 child。在目标元素上,监听器触发的顺序是按代码注册的顺序,所以先触发了捕获监听器,然后是冒泡监听器。
  4. 事件开始上浮: 完成目标阶段后,事件开始冒泡,从 child 上浮到 parent,触发了 parent 的冒泡监听器。
  5. 继续上浮: 事件继续上浮到 grandparent,触发了它的冒泡监听器,旅程结束。

通过这个简单的实验,事件流的完整"V"字形路径被清晰地展现在我们面前。理解这个模型是掌握事件委托、阻止事件传播等高级技巧的基础。

基于 VitePress 的本地知识库