事件传播\捕获\冒泡\委托分别是什么?
Hi~,我是一碗周,一个在舒适区垂死挣扎的前端,如果写的文章有幸可以得到你的青睐,万分有幸~~
🍓 写在前面
说道DOM事件,肯定离不开事件传播\捕获\冒泡\委托这些花里胡哨的东西,这些东西还是事件中比较重要的内容,这篇文章会来介绍一下这些花里胡哨的东西。
🥭 事件传播
事件传播也叫事件流,就是当触发某个元素的事件时,事件会按照DOM结构树进行传播,传播的过程如下:
- 捕获阶段:按照DOM结构由
window
对象向下的顺序传播,直到目标元素为止,这也就是事件捕获; - 目标阶段:该阶段就是值目标元素触发当前事件;
- 冒泡阶段:按照DOM结构树由目标元素向上的顺序传播,直到
window
对象,这也就是事件冒泡。
这三个阶段的过程如下所示:
上图就是事件传播的一个过程。
🍇 事件捕获与冒泡
上面那张图中DOM结构如果用HTML表示如下:
<html>
<body>
<div id="div">
<p id="p">
<a id='a'></a>
</p>
</div>
</body>
</html>
当我们点击<a>
这个元素的时候,事件会从Window对象开始逐层传递,直到<a>
触发click
事件,然后执行我们的回调函数;然后执行完毕之后会逐层向上冒泡,然后就会执行为<div>
以及父元素绑定的事件处理函数。
事件处理函数如下:
window.addEventListener('click',()=>{
console.log('window的click触发了')
})
document.addEventListener('click',()=>{
console.log('document的click触发了')
})
document.documentElement.addEventListener('click',()=>{
console.log('html的click触发了')
})
document.body.addEventListener('click',()=>{
console.log('body的click触发了')
})
document.getElementById('div').addEventListener('click',()=>{
console.log('div的click触发了')
})
document.getElementById('p').addEventListener('click',()=>{
console.log('p的click触发了')
})
document.getElementById('a').addEventListener('click',()=>{
console.log('a的click触发了')
})
此时点击<a>
元素事件函数执行结果如下:
a的click触发了
p的click触发了
div的click触发了
body的click触发了
html的click触发了
document的click触发了
window的click触发了
可见,事件的函数的执行顺序是根据DOM结构依次向上的触发的,直到window
为止。
✈️ addEventListener的第三个参数
addEventListener
的第三个参数可以控制事件函数的执行是在捕获阶段还是冒泡阶段,默认为false
也就是事件在冒泡阶段执行,如果没true
则会在捕获阶段执行事件处理函数,如下所示:
window.addEventListener('click',()=>{
console.log('window的click触发了')
}, true)
document.addEventListener('click',()=>{
console.log('document的click触发了')
}, true)
document.documentElement.addEventListener('click',()=>{
console.log('html的click触发了')
}, true)
document.body.addEventListener('click',()=>{
console.log('body的click触发了')
}, true)
document.getElementById('div').addEventListener('click',()=>{
console.log('div的click触发了')
}, true)
document.getElementById('p').addEventListener('click',()=>{
console.log('p的click触发了')
}, true)
document.getElementById('a').addEventListener('click',()=>{
console.log('a的click触发了')
}, true)
执行结果正好与前面的结果是相反的。
🐬 取消事件冒泡
取消事件冒泡可以通过event
对象提供的stopPropagation()
方法,示例如下:
document.getElementById('a').addEventListener('click',(e)=>{
e.stopPropagation()
console.log('a的click触发了')
})
此时点击<a>
仅仅只有一个输出。
🦋 事件委托
事件委托
当为大量的HTML元素注册相同事件,并且事件的句柄逻辑完全相同,会造成页面速度下降。不过,事件流允许这些HTML元素的共同父级注册事件。这种方式称为事件委托。
我们先看一段代码
<body>
<div id="container">
<button id="btn1">按钮</button>
<button id="btn2">按钮</button>
<button id="btn3">按钮</button>
</div>
<script>
var btn1 = document.getElementById('btn1')
btn1.addEventListener('click', function () {
console.log('我是按钮')
})
var btn2 = document.getElementById('btn2')
btn2.addEventListener('click', function () {
console.log('我是按钮')
})
var btn3 = document.getElementById('btn3')
btn3.addEventListener('click', function () {
console.log('我是按钮')
})
</script>
</body>
现在我们通过事件委托来改写这个代码,代码如下
var container = document.getElementById('container')
container.addEventListener('click', function (event) {
var target = event.target
// 当前点击的为 button 时,才会触发下面逻辑代码
if (target.nodeName == "BUTTON") {
console.log('我是按钮')
}
})
我们可以明显看到,代码量的节省,但是最终的运行结果是一样的。
如果在10000万个单元格中为每个单元格绑定事件那就是需要注册10000个事件?很显然不合适,这时候事件委托的作用就体现出来了。
🥪 写在最后
这里简单的回顾了事件传播\捕获\冒泡\委托,也是比较基础的一些内容,不过还是很容易被忽略的,这里做了下总结,方便记忆。
转载自:https://juejin.cn/post/7204844328110522423