一、DOM事件响应链
事件响应链分为三个阶段:捕获阶段、目标阶段、冒泡阶段。
<div class="fa">
<div class="son">
<button>按钮</button>
</div>
</div>
<script>
let fa = document.querySelector('.fa')
let son = document.querySelector('.son')
let btn = document.querySelector('.son button')
fa.addEventListener('click',()=>{
console.log('fa被点了')
})
son.addEventListener('click',()=>{
console.log('son被点了')
})
btn.addEventListener('click',()=>{
console.log('button被点了')
})
</script>
当我们点击按钮之后,事件处理函数执行,打印结果,但可以发现,外面两层的div的事件也执行了,且顺序是在button之后。div的事件会执行是因为button是他们的后代元素,点击button其实相当于也点了他们,但打印顺序就是因为事件响应链的关系。
事件响应链
由图可以看出,当事件触发后,会先来到document对象,看其有没有绑定事件,有的话放在一边,然后到HTML,看有没有事件,然后是body,根据DOM树的层级往下走,这是捕获阶段。当来到我们点击的对象时,就不再往下走,同时执行这个对象的事件处理函数,这是目标阶段。执行之后就往上走,也就是先前的步骤倒退。找到目标元素的父元素,有绑定事件就执行处理函数,然后再往上,遇到事件就执行,这是冒泡阶段。
二、设置事件执行阶段
事件默认在冒泡阶段执行。所以点击button执行函数后,执行了son的事件处理函数,最后执行了fa的事件处理函数。
addEventListener有三个参数,第一个为事件类型,第二个为事件处理函数,第三个为布尔值。默认false,表示在冒泡阶段触发。
当fa的绑定事件的第三个参数设为true后,就是在捕获阶段触发。所以再次点击button,先执行了fa的事件处理函数,然后是目标button和冒泡阶段的son。
但有时候其实希望点击一个子元素后就执行这个事件处理函数,祖先元素即使有事件也不要执行,这时候我们就可以阻止事件冒泡——调用事件对象的stopPropagation()方法。
注意:阻止冒泡是让是让事件链的执行到达目标阶段后就停止。设置事件在捕获阶段执行不叫阻止冒泡,它还是会执行冒泡阶段,只是事件已经在捕获阶段执行,所以冒泡阶段看不出现象。