DOM的事件流程

DOM的事件流程分为三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段

  1. 首先发生的是事件捕获,为截获事件提供了机会。
  2. 然后是实际的目标接收到事件。
  3. 然后冒泡阶段发生,事件又传播回文档。

第一阶段:事件捕获。

所谓的事件捕获就是从最大范围(document)开始,一级一级往下找,知道找到最精确的事件源(触发事件的节点,如图div节点)为止的过程。虽然大多数浏览器都支持事件捕获,但很少有人使用。

第二阶段:事件触发。

找到了事件源,接下来就应当执行绑定的相应的事件(如果有的话)。

第三阶段:事件冒泡。

事件源执行完事件处理后。这个类型的事件会向祖先节点传递,直到document(包括document)(当然是在事件冒泡没有被阻止的前提下,后续的分析都是基于这个前提下)。也就是说事件源的每一个祖先节点都会触发同类事件。

想象一下,把事件源a触发click事件的处理委托给其祖先节点b。当a节点触发click,事件冒泡到b的时候,b节点也触发click事件,然后b一看事件列表中有一个委托事件,这个委托事件保存了委托节点的选择器,这个选择器所匹配节点就是事件源a,那么b马上执行这个委托事件。

什么是事件委托?

事件委托——利用事件冒泡机制,给祖先元素绑定事件,用来监听子元素的事件,将子元素的事件要做的事写到祖先元素的事件里,也就是将子元素的事件处理程序写到祖先元素的事件处理程序中。

事件委托的好处:

(1)事件委托技术可以避免对每个字元素添加事件监听器,减少操作DOM节点的次数,从而减少浏览器的重绘和重排,提高代码的性能。

设想一下,假如一个div元素的子元素中有成百上千个a标签,然后,我们要监听a标签的点击事件,如果在每个a标签上绑定元素,那么量多么大的工作,而且如果是动态添加的元素,我们还无法做到正确的监听,但是有了时间委托技术,我们完全可以在其祖先元素div上监听点击事件,然后判断事件触发的源头,进而去执行相应的事件处理程序,即可满足需求。如此,减少操作DOM节点的次数,从而减少浏览器的重绘和重排,提高了代码的性能。

(2)使用事件委托,只有父元素与DOM存在交互,其他的操作都是在JS虚拟内存中完成的,这样就大大提高了性能。

什么时候用事件委托?

一般情况下,当子元素有很多(或者说有大量子元素来自于于动态增删的时候),并且需要对子元素的事件进行监听的时候,这时候,我们推荐使用事件委托。

事件委托的实现

直接看代码吧:

1
2
3
4
5
<div id="big">
<div id="middle">
<div id="small"></div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
#big{
width: 400px;
height: 400px;
overflow: hidden;
background-color: pink;
}
#middle{
width: 200px;
margin: 25% auto;
height: 200px;
overflow: hidden;
background-color: yellowgreen;
}
#small{
width: 100px;
height: 100px;
margin: 25% auto;
background-color: deepskyblue;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
let big = document.getElementById('big')
big.eventList = []
let middle = document.getElementById('middle')
let small = document.getElementById('small')

big.eventList.push({trigger:small2, target: small})
big.eventList.push({trigger:middle2, target: middle})

big.onclick = function (e) {
console.log("big clicked!")
this.eventList.forEach(element => {
if (element.target === e.target) {
element.trigger()
}
});
}
function middle2() {
console.log("middle clicked!")
}
function small2() {
console.log("small clicked!")
}
</script>

页面展示如下:

当我们点击最外层big元素的时候,输出如下:

很明显的可以看到,只有big绑定的click事件的事件处理程序执行了。

当我们点击最内层的small元素时,输出如下:

可以很明显的看到我们想要的当small元素被点击时,samll2函数被触发,而此时click事件绑定在small元素的祖先元素big上面,由此就实现了事件委托。

同理,可以实现,点击middle元素,利用事件委托将click事件绑定在其父元素big元素上,进而当middle被点击时,触发middle2函数。

当点击middle元素时,输出如下:

由此,我们就简单的模拟实现了事件委托,可以很明显的发现,实现事件委托的主要手段是利用事件冒泡机制加上事件对象的target属性判断事件触发的源头,进而实现事件委托。需要提醒大家的是,IE浏览器的事件对象并不支持target属性,在IE中对应的是srcElement,这一点请大家注意。