在JavaScript的DOM事件体系中,事件冒泡和捕获是事件流的两个核心阶段,决定了事件在DOM树中传递的顺序,两者的差异直接影响事件的处理逻辑和最终效果。

事件流的基本流程
当一个事件发生在DOM元素上时,事件不会只停留在触发元素上,而是会按照特定顺序在DOM树中传递,完整的事件流包含三个阶段:
- 捕获阶段:事件从最顶层的window对象向下传递到目标元素
- 目标阶段:事件到达触发事件的目标元素
- 冒泡阶段:事件从目标元素向上传递回最顶层的window对象
其中捕获和冒泡是开发者最常接触的两个阶段,也是两者区别的核心所在。
事件冒泡的定义与特点
事件冒泡是指事件触发后,从目标元素开始,依次向上触发父元素、祖先元素上绑定的同类型事件,直到到达DOM树的最顶层。这是大多数浏览器默认的事件传递方式,也是日常开发中最常遇到的事件行为。
我们可以通过以下代码示例直观看到事件冒泡的效果:
<!DOCTYPE html>
<html>
<head>
<style>
.outer { width: 200px; height: 200px; background: #f00; padding: 20px; }
.inner { width: 100px; height: 100px; background: #0f0; }
</style>
</head>
<body>
<div class="outer">
外层容器
<div class="inner">内层容器</div>
</div>
<script>
const outer = document.querySelector('.outer');
const inner = document.querySelector('.inner');
// 给外层容器绑定点击事件,默认使用冒泡阶段触发
outer.addEventListener('click', () => {
console.log('外层容器被点击');
});
// 给内层容器绑定点击事件
inner.addEventListener('click', () => {
console.log('内层容器被点击');
});
</script>
</body>
</html>点击内层容器时,控制台会先输出「内层容器被点击」,再输出「外层容器被点击」,这就是事件冒泡的典型表现,内层触发的事件向上传递到了外层容器。
事件捕获的定义与特点
事件捕获和冒泡的顺序完全相反,它是指事件触发后,从最顶层的window对象开始,依次向下传递,先触发祖先元素的同类型事件,最后才到达目标元素。捕获阶段在事件流中早于冒泡阶段执行。
要使用事件捕获,只需要在绑定事件时,将addEventListener的第三个参数设置为true即可,默认情况下这个参数是false,代表使用冒泡阶段触发。
以下是对上面示例的修改,开启捕获模式:
const outer = document.querySelector('.outer');
const inner = document.querySelector('.inner');
// 第三个参数设为true,使用捕获阶段触发事件
outer.addEventListener('click', () => {
console.log('外层容器被点击(捕获阶段)');
}, true);
// 内层容器仍然使用默认的冒泡阶段
inner.addEventListener('click', () => {
console.log('内层容器被点击(冒泡阶段)');
});此时点击内层容器,控制台会先输出「外层容器被点击(捕获阶段)」,再输出「内层容器被点击(冒泡阶段)」,说明捕获阶段的事件先执行,之后才进入冒泡阶段。
事件冒泡与捕获的核心区别
两者的差异主要体现在以下几个方面:
| 对比维度 | 事件冒泡 | 事件捕获 |
|---|---|---|
| 传递方向 | 从目标元素向上传递到顶层祖先 | 从顶层祖先向下传递到目标元素 |
| 执行顺序 | 在捕获阶段之后执行 | 在冒泡阶段之前执行 |
| 默认触发阶段 | addEventListener默认使用冒泡 | 需要手动设置第三个参数为true才生效 |
| 适用场景 | 事件委托、子元素触发后需要父元素联动的场景 | 需要在子元素触发前先处理父元素逻辑的场景 |
如何阻止事件冒泡
在某些场景下,我们可能不希望事件继续向上冒泡触发父元素的事件,可以通过事件对象的stopPropagation方法实现:
const inner = document.querySelector('.inner');
inner.addEventListener('click', (e) => {
// 阻止事件继续冒泡
e.stopPropagation();
console.log('内层容器被点击,事件不会向上传递');
});此时点击内层容器,只会触发内层的点击事件,外层的点击事件不会被触发。
实际开发中的选择建议
日常开发中,事件冒泡的使用频率远高于事件捕获,因为冒泡天然支持事件委托:只需要给父元素绑定一次事件,就能处理所有子元素的同类型事件,减少事件绑定的数量,提升性能。而事件捕获通常用于一些特殊场景,比如需要在子元素事件触发前先执行父元素的校验逻辑,或者需要优先处理顶层容器的事件时使用。
需要注意,不管是冒泡还是捕获,都可以通过stopPropagation阻止事件的后续传递,但是无法区分当前处于哪个阶段,只能按照事件流的默认顺序执行。
事件冒泡事件捕获JavaScript事件流DOM事件修改时间:2026-06-05 02:18:50