事件冒泡和捕获是dom事件流天然存在的两个阶段,冒泡默认启用(addeventlistener第三个参数为false),事件从目标元素逐层向上触发父级监听器;捕获需显式设置true,事件从外向内传播,在目标前介入;二者共存于完整事件流(捕获→目标→冒泡)中。

事件冒泡和捕获不是“选哪个更好”的问题,而是浏览器事件流中**天然存在的两个阶段**——你写的每个 addEventListener 都会落在其中一个阶段里,不显式指定就默认走冒泡。
为什么 click 会触发父元素的监听器?
因为 DOM 事件默认走冒泡阶段:点击子元素后,事件从目标元素出发,逐层向上通知所有祖先节点(div → body → document),只要它们绑了同类型监听器且没被阻止,就会执行。
- 冒泡是默认行为,
addEventListener('click', handler)等价于addEventListener('click', handler, false) - 即使父元素没显式监听 click,只要它有监听器,且没调用
e.stopPropagation(),就会被触发 - 注意:表单元素(如
input)的某些事件(如change)不冒泡,但click、keydown等绝大多数用户交互事件都冒泡
如何让事件先在 body 上处理,再落到按钮?
用捕获阶段:把第三个参数设为 true,让监听器在事件“下沉”途中就介入。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
-
document.body.addEventListener('click', () => console.log('body 捕获'), true)会在事件到达目标前执行 -
button.addEventListener('click', () => console.log('button 目标'), false)在目标阶段执行(此时捕获已结束) - 捕获监听器的执行顺序严格按 DOM 层级从外到内,和绑定顺序无关
- 别混淆:
true不代表“优先”,只是阶段不同;冒泡监听器依然会在之后执行
stopPropagation 和 stopImmediatePropagation 有什么区别?
e.stopPropagation() 只阻断当前事件流的后续传播(比如阻止冒泡到父元素),而 e.stopImmediatePropagation() 还会阻止**同一元素上其他同类型监听器**的执行。
- 假设一个按钮绑了两个 click 监听器:
A和B,都在冒泡阶段 - 如果
A里调用了e.stopImmediatePropagation(),那么B根本不会运行 - 但如果只调用
e.stopPropagation(),B仍会执行,只是父元素收不到这个事件 - 这个差异在事件委托或第三方库注入监听器时特别关键——你不希望自己的拦截逻辑被绕过
事件委托为什么依赖冒泡?
事件委托的本质,就是把子元素的事件监听逻辑“上收”到父容器,靠的就是冒泡把子元素的事件带到父层来处理。
- 例如:
ul.addEventListener('click', e => { if (e.target.matches('li')) { /* 处理 li 点击 */ } }) - 动态添加的
li不需要重新绑定事件,因为点击它仍会冒泡到ul - 捕获阶段也能做类似事,但实际极少用——因为捕获时
e.target还没到目标元素,你拿到的是祖先节点,无法准确判断点击的是哪个子项 - 所以事件委托几乎总是配合冒泡 +
e.target判断来实现
真正容易被忽略的是:事件流有三个阶段(捕获 → 目标 → 冒泡),而 useCapture 参数只控制监听器注册在捕获还是冒泡阶段,目标阶段的监听器永远都会执行,且不受 stopPropagation 影响——哪怕你在捕获阶段就调了它,目标阶段的监听器照常运行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










