事件冒泡 与 事件捕获
前端工作开发中一定会遇到事件冒泡的时候,比如我想点击元素内部的button,但是外部元素上面同时也绑定着chick事件,这种时候就会遇到事件冒泡现象。 如下面的例子:
<body>
<div class="outer">
<div class="middle">
<div class="inner">
<button onclick="console.log(this)">click me</button> <!-- 点击这里 -->
</div>
</div>
</div>
</body>
<script type="module">
//分别给每一层的元素都添加点击事件
const $ = (selector) => document.querySelector(selector)
console.log($)
$('.outer').addEventListener("click",e=>{
console.log(`%c outer ${e}`,'color:orange')
},false)
$('.middle').addEventListener("click",e=>{
console.log(`%c middle ${e}`,'color:palegreen')
},false)
$('.inner').addEventListener("click",e=>{
console.log(`%c inner ${e}`,'color:pink')
},false)
</script>
控制台输出
从控制台输出可以看出我们点击了botton,从内而外 依次出发了inner、middle、outer上的click事件,,这种现象就是事件冒泡。
事件捕获与事件冒泡的现象正好相反,是由外而内的触发
<body>
<div class="outer">
<div class="middle">
<div class="inner">
<button onclick="console.log(this)">click me</button> <!-- 点击这里 -->
</div>
</div>
</div>
</body>
<script type="module">
const $ = (selector) => document.querySelector(selector)
console.log($)
$('.outer').addEventListener("click",e=>{
console.log(`%c outer ${e}`,'color:orange')
},true)
$('.middle').addEventListener("click",e=>{
console.log(`%c middle ${e}`,'color:palegreen')
},true)
$('.inner').addEventListener("click",e=>{
console.log(`%c inner ${e}`,'color:pink')
},true)
</script>
控制台输出
上面两种写法的区别在于addEventLinstener的第三个参数不一样,true的时候是事件捕获,false 的时候是事件冒泡。
这里面我们打印了参数e
我们可能会分不清e.target 和 e.currentTarget 的情况,我们看下面的代码可能回给你带来启示:
<body>
<div class="outer">
<div class="middle">
<div class="inner">
<button onclick="console.log(this)">click me</button>
</div>
</div>
</div>
</body>
<script type="module">
const $ = (selector) => document.querySelector(selector)
console.log($)
$('.outer').addEventListener("click",e=>{
console.log(`%c e.target`,'color:orange',e.target)
console.log(`%c e.currentTarget`,'color:orange', e.currentTarget)
// console.log(`%c outer ${e}`,'color:orange')
},false)
$('.middle').addEventListener("click",e=>{
// console.log(`%c e.target`,'color:palegreen',e.target)
// console.log(`%c e.currentTarget`,'color:palegreen',e.currentTarget)
// console.log(`%c middle ${e}`,'color:palegreen')
},false)
$('.inner').addEventListener("click",e=>{
// console.log(`%c e.target`,'color:pink',e.target)
// console.log(`%c e.currentTarget`,'color:pink',e.currentTarget)
// console.log(`%c inner ${e}`,'color:pink')
},false)
</script>
当我们分别点击outer、middle和inner 的时候打印如下
我们可以发现currentTarget始终都是一个,而target随着我们的点击而改变
这里我们就可以了解到了,e.target实时点击的那个对象,而currentTarget是我们绑定addEventListener的当前对象。
聊到事件冒泡就不得不提如何阻止事件冒泡。
阻止事件冒泡
大多数时候我们是不希望事件向上冒泡的,我们应该如何阻止这种行为呢?
<body>
<div class="outer">
<div class="middle">
<div class="inner">
<button onclick="console.log(this)">click me</button>
</div>
</div>
</div>
</body>
<script type="module">
const $ = (selector) => document.querySelector(selector)
console.log($)
$('.outer').addEventListener("click",e=>{
// console.log(`%c e.target`,'color:orange',e.target)
// console.log(`%c e.currentTarget`,'color:orange', e.currentTarget)
e.stopPropagation()
console.log(`%c outer ${e}`,'color:orange')
},false)
$('.middle').addEventListener("click",e=>{
// console.log(`%c e.target`,'color:palegreen',e.target)
// console.log(`%c e.currentTarget`,'color:palegreen',e.currentTarget)
e.stopPropagation()
console.log(`%c middle ${e}`,'color:palegreen')
},false)
$('.inner').addEventListener("click",e=>{
// console.log(`%c e.target`,'color:pink',e.target)
// console.log(`%c e.currentTarget`,'color:pink',e.currentTarget)
e.stopPropagation()
console.log(`%c inner ${e}`,'color:pink')
},false)
</script>
可以看到我们这里用到了e.stopPropagation(),我们再来点击,就会发现冒泡事件被阻止了。
在vue中我们也经常会遇到这个问题,通常我们是怎么解决的呢?
vue提供可绑定事件的语法糖例如:@click,还提供了修饰符.stop 用来阻止事件传递的,所以我们像下面这样写就可以了
<div @click.stop='clickEvent'></div>
好了,以上就是对事件冒泡的总结和理解。