当一个HTML元素触发一个事件时,该事件会在元素结点与根结点之间的路径传播。传播按顺序分为三个阶段:捕获阶段、目标阶段、冒泡阶段,这个传播过程就是 DOM 事件流。
事件捕获就是当一个HTML元素触发一个事件时,它的祖先节点都会收到捕获该事件。
- 通过设置
addEventListener
的第三个参数可以决定事件是否在捕获阶段触发。 - 通过
event.stopPropagation()
可以阻止事件冒泡。
一、定义
DOM结构是一个树型结构,当一个HTML元素触发一个事件时,该事件会在元素结点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为 DOM 事件流(DOM event flow )。
传播按顺序分三个阶段:
- 捕获阶段(capture phrase,从根节点document到目标节点,即最近的、最精确的元素节点,依次捕获触发的事件)
- 目标阶段(target phrase,目标节点上的事件触发按代码执行顺序触发)
- 冒泡阶段(bubbling phrase,从目标节点到根节点 )
默认在捕获阶段不触发事件
二、演示
<div>
<button>点击</button>
</div>
现在有一个div节点,div节点里面有一个button节点。
let div = document.getElementsByTagName('div');
div.addEventListener('click',(e)=>{
console.log('div')
})
let button = document.getElementsByTagName('button');
button.addEventListener('click',(e)=>{
console.log('button')
})
现在我们只看div
节点和button
节点,当我们点击button
时会先后打印出button
、div
,因为addEventListener
方法默认是让事件在冒泡阶段触发。如果我们设置addEventListener
第三个参数useCapture
的值为true
,就会让事件在捕获阶段触发:
div.addEventListener('click',(e)=>{
console.log('div')
},true)
这个时候点击button
就会先触发div
的click
事件,再触发button
的click
事件。
三、阻止冒泡:event.stopPropagation()
stopPropagation
方法会让事件传播到目标阶段后停止传播,所以也叫阻止冒泡。相当于让事件流只剩下捕获阶段和目标阶段。所以下面的代码会先后打印出div1
、 button
。
button.addEventListener('click',(e)=>{
e.stopPropagation()
console.log('button')
})
div.addEventListener('click',(e)=>{
console.log('div1')
},true)
div.addEventListener('click',(e)=>{
console.log('div2')
})
也可以通过setTimeout
来调整事件内代码执行顺序
四、马上停止传播:event.stopImmediatePropagation();
有个特例,如果目标阶段的节点绑定了多个事件,它们不会区分捕获和冒泡,事件触发的顺序为代码执行的顺序。
而且event.stopPropagation()
在目标阶段不会生效。如果目标阶段有 a、b、c 三个触发事件会按序执行,在 b 事件里设置event.stopPropagation()
并不会影响 c 事件的触发。 但是如果在 b 事件里设置event.stopImmediatePropagation()
后 ,事件触发到b之后就会停止触发后面的所有事件。
五、发散
e.preventDefault() 阻止默认事件
在Vue中使用修饰符来实现
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 开启捕获阶段 -->
<div @click.capture ="doThis"></div>
网友评论