事件冒泡基本原理
一、核心概念
事件冒泡(Event Bubbling)是 JavaScript DOM 事件流的核心机制之一:当一个元素触发某事件(如点击、输入)后,该事件会从目标元素开始,逐级向上传播到它的父元素、祖先元素,直至 window 对象。
简单理解:就像水中的气泡,从水底(目标元素)向上冒到水面(window)。
二、DOM 事件流的三个阶段
完整的 DOM 事件流包含 3 个阶段,事件冒泡是最后一环:
| 阶段 | 说明 |
|---|---|
| 捕获阶段 | 事件从 window 开始,向下逐层传递到目标元素(路过的祖先元素会触发捕获阶段的监听器)。 |
| 目标阶段 | 事件到达真正触发的目标元素,执行该元素的事件监听器。 |
| 冒泡阶段 | 事件从目标元素开始,向上逐层传递回 window(路过的祖先元素会触发冒泡阶段的监听器)。 |
注意:通过
addEventListener绑定事件时,第三个参数useCapture可控制监听阶段:
false(默认):在冒泡阶段触发监听器;true:在捕获阶段触发监听器。
三、事件冒泡的直观示例
假设有如下 HTML 结构(三层嵌套):
1 | |
给三个元素都绑定冒泡阶段的点击事件:
1 | |
点击“儿子元素”时,控制台输出顺序:
1 | |
这就是事件冒泡的典型表现:事件从目标元素“向上冒”,依次触发父级、祖父级的同类型事件。
四、事件对象的关键属性
通过事件对象 event,可精准控制冒泡过程:
| 属性 | 说明 |
|---|---|
event.target |
真正触发事件的目标元素(始终不变,如上例中的 son)。 |
event.currentTarget |
当前正在处理事件的元素(随冒泡变化,如 father、grandpa)。 |
event.eventPhase |
当前事件流阶段:1(捕获)、2(目标)、3(冒泡)。 |
五、阻止事件冒泡
若不想让事件继续向上冒泡,可通过以下方法阻止:
1. event.stopPropagation()
最常用的方法,阻止事件继续向上冒泡,但不影响当前元素的其他监听器。
1 | |
此时点击“儿子元素”,只会触发自身的事件,爸爸和爷爷的事件不会执行。
2. event.stopImmediatePropagation()
更彻底的方法:不仅阻止冒泡,还阻止当前元素后续绑定的同类型监听器。
1 | |
注意:阻止冒泡 ≠ 阻止默认行为(如链接跳转、表单提交)。阻止默认行为需用
event.preventDefault()。
六、事件冒泡的应用:事件委托(代理)
事件冒泡最实用的场景是事件委托:利用冒泡机制,给父元素绑定一个监听器,统一处理所有子元素的同类型事件。
示例:动态列表的点击处理
假设有一个动态添加项的列表:
1 | |
不用给每个 li 单独绑定事件,直接给父级 ul 绑定:
1 | |
事件委托的优势:
- 减少内存占用:无需给每个子元素绑定监听器;
- 适配动态元素:后续通过 JS 新增的子元素,也能自动触发事件。
七、注意事项
不是所有事件都冒泡:
- 不冒泡的事件:
focus、blur、load、unload、mouseenter、mouseleave(可用mouseover/mouseout替代,它们会冒泡); - 会冒泡的事件:
click、input、keydown、keyup、mouseover、mouseout等。
- 不冒泡的事件:
冒泡顺序与 DOM 结构一致:
即使子元素通过 CSS 定位(如position: absolute)脱离文档流,冒泡仍严格按照 HTML 中的嵌套顺序向上传播。性能考虑:
避免给最顶层的window/document绑定过多复杂的冒泡事件,可能导致页面卡顿。