事件流就是事件触发后,从哪开始走、到哪结束。捕获从外到内,冒泡从内到外。
核心三个阶段:
① 捕获阶段——从 document 往目标走(外→内)
② 目标阶段——到达目标元素
③ 冒泡阶段——从目标往外走(内→外)
坑:addEventListener 第三个参数 true=捕获 false=冒泡、stopPropagation 阻止冒泡、默认冒泡行为。
// 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);
在任何一级喊一句 e.stopPropagation(),水波就停在这,不再往下传。
// 点 inner 时跑这个函数
inner.addEventListener('click', function (e) {
// 在这里一喊停,外面的 middle/outer 就不再响了
e.stopPropagation();
});
它不是阻止往上传,是拦默认动作——比如链接自己要跳转、表单自己要提交。
// 抓到那个链接
let a = document.getElementById('myLink');
// 点链接时跑这个
a.addEventListener('click', function (e) {
// 拦住它天生要做的跳转,点了也不跳
e.preventDefault();
});
如果你把这俩搞混就会出怪现象:想拦跳转却用了 stopPropagation,结果照样跳;想阻止往外传却用了 preventDefault,结果外层还是响。记住——stopPropagation 管"别再往别的节点传",preventDefault 管"别做标签本来要做的事"。表单一提交页面白屏刷新,多半是漏了 preventDefault。
① 用在哪:弹窗里点内容不希望触发外层;阻止链接默认跳转。
② 常见坑:把 stopPropagation 和 preventDefault 用混;点提交页面白屏刷新。
③ 怎么解决:断传播用 stopPropagation,拦默认动作用 preventDefault;表单提交必须拦。
| API | 作用 | 参数 | 返回值 | 代码示例 |
|---|---|---|---|---|
| addEventListener 第三参 | true 捕获/false 冒泡 | true/false | 无 | btn.addEventListener('click', fn, true); |
| e.stopPropagation() | 不让事件再往外传 | 无 | 无 | e.stopPropagation(); |
| e.preventDefault() | 拦标签默认动作(跳转/提交) | 无 | 无 | e.preventDefault(); |