事件流分捕获、目标、冒泡三阶段,单向不可逆:捕获从window至目标父级,目标阶段在元素自身触发,冒泡从目标回传至window;监听器执行顺序由注册阶段和层级决定;stoppropagation()按调用阶段截断后续传播。

事件捕获先于事件冒泡发生,中间夹着一个目标阶段;整个过程是单向、不可逆的固定流程:从外到内捕获 → 在目标元素上触发 → 从内到外冒泡。
事件流严格分三步:捕获 → 目标 → 冒泡
当你点击一个嵌套结构中的子元素(比如 <div><p><button>点我</button></p></div>),事件不是直接落在 button 上就结束,而是按以下顺序传播:
-
捕获阶段:事件从
window开始,依次经过document、、、<div>、<code><p></p>,直到抵达<button></button>的父级(但不进入 button 自身); -
目标阶段:事件到达
<button></button>元素本身,执行绑定在它上面的监听器(无论第三个参数是 true 还是 false,此时都会触发); -
冒泡阶段:事件从
<button></button>开始,依次向上经过<p></p>、<div>、<code>、、document、window。监听器注册位置决定它在哪一阶段运行
同一个事件类型,在不同元素或同一元素上注册多个监听器时,执行顺序由阶段和层级共同决定:
- 在祖先元素上设
true(捕获),比目标元素上的监听器更早执行; - 在目标元素上注册的监听器,无论用
true或false,都只在目标阶段运行一次; - 在祖先元素上设
false(冒泡),一定晚于目标阶段,且离目标越近的祖先,越早触发(比如<p></p>的冒泡监听器比<div> 的先执行)。 <h3>阻止传播会影响后续阶段,但不改变已开始的阶段</h3> <p>调用 <code>event.stopPropagation()只会中断当前方向的剩余路径:- 若在捕获阶段调用,后续的捕获节点和整个目标、冒泡阶段都不再触发;
- 若在目标阶段调用,目标之后的冒泡路径被截断,但捕获已完成、目标也已执行;
- 若在冒泡阶段某一层调用,则该层之上的祖先不再收到事件。
实际开发中怎么选捕获还是冒泡
大多数日常交互用冒泡就够了,捕获适合提前干预:
- 用冒泡实现事件委托:给
<ul></ul>绑 click,靠e.target判断是否点中了某个<li>; - 用捕获做全局拦截:在
document上用true监听 click,统一做权限检查或禁用未登录操作; - 避免误触发:父容器想响应区域点击,但不想响应内部按钮的点击,可在按钮上用
stopPropagation()阻断冒泡。
- 在祖先元素上设











