← 返回目录

11. 事件流:捕获与冒泡

事件流就是事件触发后,从哪开始走、到哪结束。捕获从外到内,冒泡从内到外。

核心三个阶段:
捕获阶段——从 document 往目标走(外→内)
目标阶段——到达目标元素
冒泡阶段——从目标往外走(内→外)

坑:addEventListener 第三个参数 true=捕获 false=冒泡、stopPropagation 阻止冒泡、默认冒泡行为

11.1 互动演示(点最红那块,看谁先响)

外层 outer
中间 middle
点我 inner
这个链接点了不会跳(已拦住默认跳转)
先选模式再点 inner: 盒子按响应顺序一层层亮, 下面同步代码与顺序。

11.2 知识点逐条讲解

① 三段怎么传

// window → document → html → body → … → 你点的 → … → body → html → … → window
// 捕获(外往内) 目标阶段 冒泡(内往外)

addEventListener 第三个参数决定监听挂哪段:true = 捕获段(外先响)false/不写 = 冒泡段(内先响),默认就是冒泡。

② 亲自看先后

// 冒泡:点 inner,响应顺序 inner → middle → outer(内往外)
// 第三参 false=冒泡段,内先响
outer.addEventListener('click', fn, false);

// 捕获:再点 inner,顺序 outer → middle → inner(外往内)
// 第三参 true=捕获段,外先响
outer.addEventListener('click', fn, true);

③ stopPropagation:不让它再往外传

在任何一级喊一句 e.stopPropagation(),水波就停在这,不再往下传。

// 点 inner 时跑这个函数
inner.addEventListener('click', function (e) {
  // 在这里一喊停,外面的 middle/outer 就不再响了
  e.stopPropagation();
});

④ preventDefault:不让标签干它"天生要干的事"

不是阻止往上传,是拦默认动作——比如链接自己要跳转、表单自己要提交。

// 抓到那个链接
let a = document.getElementById('myLink');

// 点链接时跑这个
a.addEventListener('click', function (e) {
  // 拦住它天生要做的跳转,点了也不跳
  e.preventDefault();
});

如果你把这俩搞混就会出怪现象:想拦跳转却用了 stopPropagation,结果照样跳;想阻止往外传却用了 preventDefault,结果外层还是响。记住——stopPropagation 管"别再往别的节点传",preventDefault 管"别做标签本来要做的事"。表单一提交页面白屏刷新,多半是漏了 preventDefault。

实战:用在哪 / 常见坑 / 怎么解决

① 用在哪:弹窗里点内容不希望触发外层;阻止链接默认跳转。

② 常见坑:把 stopPropagation 和 preventDefault 用混;点提交页面白屏刷新。

③ 怎么解决:断传播用 stopPropagation,拦默认动作用 preventDefault;表单提交必须拦。

一句话记住:事件流=捕获(外往内)+目标+冒泡(内往外);第三参 true 捕获、false 冒泡;stopPropagation 断传播,preventDefault 拦默认动作(跳转/提交)。

本页重点

API作用参数返回值代码示例
addEventListener 第三参true 捕获/false 冒泡true/falsebtn.addEventListener('click', fn, true);
e.stopPropagation()不让事件再往外传e.stopPropagation();
e.preventDefault()拦标签默认动作(跳转/提交)e.preventDefault();