JavaScript的冒泡事件和捕获事件是每一个前端开发者都会经常遇到的问题。最近遇到这个问题时,发现自己对它们还是一知半解,现在是时候该彻底的弄懂它们了。
1、定义
一个事件发生后,会在子元素和父元素之间传播。这种传播分成三个阶段:
- 第一阶段:从window对象传导到目标节点(外层传到内层),称为“捕获阶段”。
- 第二阶段:在目标节点上触发,称为“目标阶段”。
- 第三阶段:从目标节点传导回window对象(从内层传回外层),称为“冒泡阶段”
2、应用
本文中我们用addEventListener() 方法来演示JavaScript的冒泡/捕获事件。
addEventListener(type, listener[, useCapture])方法接收三个参数,type:事件名称,listener:监听函数(如click、keydown等),useCapture:布尔值,表示监听函数是否在捕获阶段触发,默认为false(监听函数在冒泡阶段触发)。
纸上得来总觉浅,实践出真知。
<style>
#parent{
width: 200px;
height: 200px;
border: 1px solid red;
}
#child{
width: 50px;
height: 50px;
border: 1px solid blue;
}
</style>
<div id="parent">
<div id="child"></div>
</div>
<script>
var parent = document.getElementById('parent');
var child = document.getElementById('child');
parent.addEventListener('click',function(e){
console.log('我是parent');
},false)
child.addEventListener('click',function(e){
console.log('我是child');
},true)
</script>
在上面的代码中,我们将parent.addEventListener的第三个参数设为false,表示监听函数在冒泡阶段触发。也就是说会先打印child的日志,后打印parent的日志。
打印结果和我们想的一样。
下面我们把parent.addEventListener的第三个参数设为true,表示监听函数在捕获阶段触发。相信你一定知道打印结果了吧。
知道了冒泡事件和捕获事件的应用场景,但是这并没有结束。在类似于上面的场景中,很多时候我们在点击child这个div的时候,不想触发parent的点击事件,这就是我们平常说的阻止冒泡事件。如何阻止呢?直接在child的监听函数中调用事件对象的stopPropagation方法就可以。
parent.addEventListener('click',function(e){
console.log('我是parent');
},false)
child.addEventListener('click',function(e){
e.stopPropagation()
console.log('我是child');
},false)
那么如何阻止捕获事件呢?同样也是调用stopPropagation方法,不过这次是在parent的监听函数中调用,同时把parent的监听函数第三个参数设为true。
parent.addEventListener('click',function(e){
e.stopPropagation()
console.log('我是parent');
},true)
child.addEventListener('click',function(e){
console.log('我是child');
},false)
结果就只会打印parent监听函数的日志了,实际工作中很少会有这样的需求,这里只做演示使用。
总结:阻止冒泡事件就是阻止内层的事件不再往外层传播,所以stopPropagation方法要加在内层的监听函数中,同时内层监听函数的第三个参数设为false;
阻止捕获事件就是阻止外层事件不再往里面传播,所以stopPropagation方法要加在外层的监听函数中,同时外层监听函数的第三个参数设为true;
3、在vue中阻止冒泡/捕获
vue中click事件默认是冒泡模式,就是先相应内层的点击事件,再相应外层的点击事件。
<div class="parent" @click="parentClick">
<div class="child" @click="childClick"></div>
</div>
methods: {
parentClick () {
console.log('我是parent')
},
childClick () {
console.log('我是child')
}
}
比如上面的代码,点击类名为child的div,打印结果是先打印"我是child",再打印“我是parent”。在vue中如何阻止冒泡事件呢?也很简单,直接在里面的div的@click后面添加vue提供的修饰符.stop即可。
<div class="parent" @click="parentClick">
<div class="child" @click.stop="childClick"></div>
</div>
这样就只会打印“我是child”一行日志了。
同时,vue也提供了事件捕获模式的修饰符.capture,我们在外面div的@click后面加上这个修饰符
<div class="parent" @click.capture="parentClick">
<div class="child" @click="childClick"></div>
</div>
打印结果是什么呢?跟默认的冒泡模式刚好相反,先打印"我是parent",再打印“我是child”。那么问题来了,在vue中如何阻止捕获事件呢?和上面的阻止冒泡一样,只需要在.capture修饰符后面再加上.stop修饰符就可以了(修饰符是可以串联的)。
<div class="parent" @click.capture.stop="parentClick">
<div class="child" @click="childClick"></div>
</div>
打印结果就只有“我是parent”一行日志了。