1、前言
本文中的所有代码demo均在chrome浏览器下运行。
阅读本文,你将学到:
1. 浏览器默事件传播机制
2. 捕获、冒泡的机制
3. 阻止捕获、冒泡
4. `stopImmediatePropagation`和`stopPropagation`区别
2、事件传播机制
2.1 浏览器事件默认传播机制
嵌套元素,默认的事件传播方向是什么(默认情况下,addEventListener第三个参数为false),
先写两个元素div1、div2,两个元素是父子关系:
<style>
#div1 {
width: 300px;
padding: 15px;
border: 2px solid black;
}
#div2 {
width: 100px;
padding: 5px;
border: 2px solid darkorange;
}
</style>
<body>
<div id="div1">
div1
<div id="div2">
div2
</div>
</div>
</body>
js部分:
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
div1.addEventListener("click", function () {
console.log("div1被点击");
});
div2.addEventListener("click", function () {
console.log("div2被点击");
});
点击div2, 依次打印:div2被点击, div1被点击,
结论:默认是从目标元素开始,冒泡执行(从里向外传播)。
2.2 手动设置在捕获或冒泡阶段执行
设置(addEventListener第三个参数为true)div1在捕获阶段执行, div2不设置第三个参数(其实不管设置了true还是false,结果是一样的)
div1.addEventListener(
"click",
function () {
console.log("div1被点击");
},
true // 新增++
);
div2.addEventListener("click", function () {
console.log("div2被点击");
});
点击div2, 依次打印:div1被点击, div2被点击,
结论:如果外层元素设置在捕获阶段执行,那么会先触发外层元素的绑定事件
3、阻止冒泡、捕获
3.1 点击div2的时候,怎么阻止事件向外层传递(阻止冒泡)?
div1.addEventListener("click", function () {
console.log("div1被点击");
});
div2.addEventListener("click", function (e) {
console.log("div2被点击");
e.stopPropagation();
// e.stopImmediatePropagation(); // 用这个也能阻止
});
只打印:div2被点击,
结论:在div2中使用stopPropagation或stopImmediatePropagation方法阻止(下边会解释这两个api的区别)。
3.2 点击div2, 如果div1、div2都在捕获阶段执行,怎么阻止捕获?
div1.addEventListener(
"click",
function (e) {
console.log("div1被点击");
e.stopPropagation();
// e.stopImmediatePropagation(); // 也能阻止捕获
},
true
);
div2.addEventListener(
"click",
function (e) {
console.log("div2被点击");
},
true
);
只打印:div1被点击,
结论:可以使用stopPropagation或stopImmediatePropagation方法阻止事件在捕获阶段传播。
4、stopImmediatePropagation
既然stopPropagation或stopImmediatePropagation都可以阻止冒泡或者阻止捕获,那有什么区别呢?
我们再给div2加一个click事件,然后在div2第一个click事件处理函数中添加stopPropagation方法,会发生什么呢?
我们先看stopPropagation:
div1.addEventListener("click", function (e) {
console.log("div1被点击");
});
div2.addEventListener("click", function (e) {
e.stopPropagation();
console.log("事件1 div2被点击");
});
div2.addEventListener("click", function (e) {
console.log("事件2 div2被点击");
});
不出意外,会依次打印:事件1 div2被点击, 事件2 div2被点击(没有打印div1被点击是因为,在div2中阻止冒泡了);
把div2的第二个click事件代码放到div2第一个click事件代码前边,会依次打印事件2 div2被点击, 事件1 div2被点击;
结论:当同一个元素绑定多个相同事件的时候,执行顺序和代码的前后顺序有关,也就是和绑定的顺序有关
接下来看stopImmediatePropagation:
div1.addEventListener("click", function (e) {
console.log("div1被点击");
});
div2.addEventListener("click", function (e) {
e.stopImmediatePropagation();
console.log("事件1 div2被点击");
});
div2.addEventListener("click", function (e) {
console.log("事件2 div2被点击");
});
只打印:事件1 div2被点击;
结论:stopImmediatePropagation会阻断相同元素的后续的相同事件的执行。
5、总结
-
Chrome浏览器的默认事件传播顺序是:目标元素 --> 向上冒泡;
-
stopImmediatePropagation和stopPropagation区别:a.
stopPropagation: 可以阻止事件在冒泡或捕获阶段传递,但是不能阻止监听同一事件的其他事件监听器被调用;b.
stopImmediatePropagation: 如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。如果在其中一个事件监听器中执行stopImmediatePropagation(),那么剩下的事件监听器都不会被调用。