首先我们要知道,DOM事件流存在三个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段
DOM标准事件流的触发的先后顺序是:先捕获再冒泡
addEventListener的第三个参数
在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件,另一个是触发事件后要执行的函数,然而addEventListener还可以传入第三个参数:
element.addEventListener(event, function, useCapture);
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。如果不写第三个参数则默认在事件冒泡阶段调用事件处理函数。
1.冒泡
事件冒泡描述了浏览器如何处理针对嵌套元素的事件。
例如:这里有三个box依次嵌套 (big、center、small)
<style>
.big {
width: 300px;
height: 300px;
background-color: red;
}
.center {
width: 200px;
height: 200px;
background-color: green;
}
.small {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
<body>
<div class="big">big
<div class="center">center
<div class="small">small</div>
</div>
</div>
</body>
接下来给最小的box添加点击事件:
let small = document.querySelector('.small')
small.addEventListener('click', (e) => {
console.log('small');
})
此时我们点击small会打印出"small"
接下来我们给center、big都加上点击事件
big.addEventListener('click', (e) => {
console.log('big');
}, false)
center.addEventListener('click', (e) => {
console.log('center');
}, false)
small.addEventListener('click', (e) => {
console.log('small');
}, false)
此时我们再次点击small,发现依次打印出了small、center、big
也就是说三个box都触发了点击事件! 在这种情况下:
- 最先触发small上的的点击事件
- 然后是small的父元素center
- 然后是center的父元素big
事件从被点击的最里面的元素冒泡而出
2.捕获
事件传播的另一种形式是事件捕获。这就像事件冒泡,但顺序是相反的:
//捕获
big.addEventListener('click', (e) => {
console.log('big');
}, true)
center.addEventListener('click', (e) => {
console.log('center');
}, true)
small.addEventListener('click', (e) => {
console.log('small');
}, true)
//冒泡
big.addEventListener('click', (e) => {
console.log('big');
}, false)
center.addEventListener('click', (e) => {
console.log('center');
}, false)
small.addEventListener('click', (e) => {
console.log('small');
}, false)
此时我们点击small,发现输出:
可以看出,事件是先进行了捕获,然后进行了冒泡
3.事件委托
事件委托也称为事件代理。就是利用事件冒泡,把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托就无法实现
例如:我们如果循环给ul里的li元素添加点击事件的话,势必会增加内存损耗,影响性能
此时可以给li的父元素ul添加点击事件 这时就相当于给每个li都绑定了点击事件