← 返回目录

11. 事件流:捕获与冒泡

点一个按钮,事件不是"只跑这一个按钮"就完了,而是像水波一样传一圈:先从最外层 html 一路传进来(这叫捕获),到了目标后再一路传回去(这叫冒泡,就是声音从里往外传)。一共三段:捕获 → 目标 → 冒泡

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

外层 outer
中间 middle
点我 inner
这个链接点了不会跳(已拦住默认跳转)
先选模式,再点 inner,看响应先后顺序。

11.2 知识点逐条讲解

① 三段怎么传

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

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

② 亲自看先后

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

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

③ stopPropagation:不让它再往外传

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

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

④ 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 拦默认动作(跳转/提交)。