事件捕获与冒泡不是选择问题,而是需按阶段合理使用:捕获从document向下至目标父级,目标阶段执行绑定在目标上的监听器,冒泡从目标向上传至document;常见误触需用e.target===e.currentTarget判断或stopPropagation()拦截,事件委托依赖冒泡,捕获适用于全局拦截、遮罩层等提前干预场景。

JS 中事件捕获与冒泡不是“选哪个更好”,而是“什么时候该用哪个”。真正踩坑的,往往不是没听过这两个词,而是绑定时没意识到第三个参数的含义,或者在嵌套结构里误判了事件触发顺序。
搞清三个阶段的执行顺序
一次点击会完整走完:捕获 → 目标 → 冒泡。哪怕你只给某个元素绑了一个监听器,它也一定落在其中某一个阶段里。
- 捕获阶段从 document 开始,逐层向下,到目标元素的父级为止(不包括目标自身)
- 目标阶段只触发目标元素本身上绑定的、对应阶段的监听器(无论 useCapture 是 true 还是 false,目标阶段都会执行)
- 冒泡阶段从目标元素开始,逐层向上传到 document
别让父元素“意外响应”子元素操作
常见问题:点按钮,结果外层弹窗关闭了;点列表项,整个卡片被选中。根源就是父级绑了 click,又没做拦截。
- 用 e.stopPropagation() 可以中途切断传播,但要谨慎——它同时阻止捕获和冒泡,可能影响其他逻辑
- 更安全的做法是判断 e.target === e.currentTarget:只有真正点中当前绑定元素时才执行,避免冒泡带来的误触发
- 如果只是想阻止默认行为(比如表单提交、链接跳转),用 e.preventDefault(),它和传播无关
事件委托必须依赖冒泡
动态生成的按钮、无限滚动的列表、频繁增删的 DOM 节点——这些场景下,逐个绑定 click 是低效且易漏的。
- 把监听器统一挂在稳定父容器上,利用冒泡让事件“浮上来”
- 在回调里用 e.target 精准识别实际被点击的子元素,比如 e.target.matches('button.delete')
- 注意:focus、blur、scroll 等事件不冒泡,不能直接用于委托;需要换方案,比如 focusin/focusout
捕获阶段的真实用途
捕获不是“冷知识”,它解决的是“提前干预”的需求。
- 全局拦截:比如在 document 捕获阶段监听所有点击,统一做权限校验或埋点,早于任何业务逻辑
- 遮罩层拦截:弹窗外层蒙层用捕获监听 click,能确保在子内容还没响应前就捕获并关闭弹窗
- 父子同绑时,若需父级先处理(如权限控制),捕获阶段天然满足“自上而下”优先级


















