javascript中的事件冒泡和事件捕获是什么?【教程】

事件冒泡和事件捕获是DOM事件流的两个固有阶段:先捕获(document→目标),后冒泡(目标→document);addEventListener第三个参数决定监听阶段,true或{capture:true}为捕获,false或省略为冒泡;stopPropagation()仅中断当前阶段传播。

事件冒泡和事件捕获的本质区别

它们是 DOM 事件流的两个相反阶段,不是两种“可选模式”,而是浏览器固有行为的两个方向。事件触发时,**先捕获(从 document 向下到目标元素),再冒泡(从目标元素向上到 document)**。默认情况下,addEventListener 绑定的是冒泡阶段的监听器。

addEventListener 的第三个参数决定监听时机

这个布尔值或选项对象直接控制监听器在哪个阶段生效:

  • true → 捕获阶段触发(例如:el.addEventListener('click', handler, true)
  • false(或省略)→ 冒泡阶段触发(最常见用法)
  • {capture: true} → 捕获阶段(现代写法,更清晰)
  • {once: true, capture: false} → 只在冒泡阶段执行一次

注意:onclick 等内联事件属性和 addEventListener 的冒

泡版都只响应冒泡阶段,无法监听捕获阶段。

阻止传播用 stopPropagation(),但要注意阶段差异

调用 event.stopPropagation() 会立即中断当前阶段的传播路径,不影响另一阶段已注册但尚未触发的监听器:

  • 在捕获阶段调用 → 后续捕获监听器不执行,但目标阶段和冒泡阶段仍照常进行
  • 在冒泡阶段调用 → 上层冒泡监听器不执行,但捕获和目标阶段已完成,不受影响
  • 若想彻底阻止所有阶段,需在捕获阶段就调用 stopPropagation()

别混淆 stopImmediatePropagation():它还会阻止同一阶段内其他已注册的监听器(哪怕同是捕获或同是冒泡)。

实际开发中哪些场景必须关注捕获阶段?

绝大多数点击、输入类交互靠冒泡就够用了,捕获阶段主要用于底层控制或全局干预:

  • 全局快捷键拦截(如 Esc 关闭模态框),在 document 捕获阶段监听,避免被子元素 stopPropagation() 阻断
  • 防止第三方组件意外阻止关键事件(比如某个 UI 库在按钮上阻止了 click 冒泡,你在 body 捕获阶段仍能捕获)
  • 实现自定义事件委托的前置逻辑(如权限校验、日志埋点),在捕获阶段统一处理再放行

捕获阶段的监听器执行顺序是从外到内,而冒泡是从内到外;这点在嵌套结构中调试事件流向时容易被忽略。