JavaScript与HTML之间的交互是通过事件来实现的,事件就是当文档或者浏览器窗口发生一些特定的交互瞬间,事件流是指从页面中接收事件的顺序。IE和Netscape开发团队提出,当你点击页面上的按钮时,你也同时点击了按钮的容器元素,甚至也点击了整个页面,但是,对于事件流,这两个团队提出了两个完全相反的事件流概念,IE认为,事件流是冒泡流,而Netscape认为事件流是捕获流。

事件冒泡

事件冒泡即由最具体的元素(文档中嵌套层次最深的节点)到最不具体的元素(文档)。
HTML结构如下

1
2
3
<div id="parent">
<div id="child"></div>
</div>

JS代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// DOM0级
child.onclick=function(){
console.log('child')
}
parent.onclick=function(){
console.log('parent')
}
document.onclick=function(){
console.log('body')
}
document.onclick=function(){
console.log('document')
}
// DOM2级
child.addEventListener('click',function(e){
console.log('child');
},false)
parent.addEventListener('click',function(e){
console.log('parent');
},false)
document.body.addEventListener('click',function(e){
console.log('body');
},false)
document.addEventListener('click',function(e){
console.log('document');
},false)

当点击child时,可以看到控制台

image.png

当点击parent时
点击parent

当点击body时

点击body

当点击document时

点击document

也就是说,click事件首先在child元素上发生,然后沿DOM树向上传播,在parent元素上发生,最后传播到document上。如果只点击parent,就从parent沿着DOM树向上传播,直到document。

冒泡过程

事件捕获

事件捕获是不太具体的节点应该最早接收事件,而最具体的节点最后接收事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
// DOM2级
child.addEventListener('click',function(e){
console.log('child');
},true)
parent.addEventListener('click',function(e){
console.log('parent');
},true)
document.body.addEventListener('click',function(e){
console.log('body');
},true)
document.addEventListener('click',function(e){
console.log('document');
},true)

当点击child时
点击child

当点击parent时
点击parent

当点击body时
点击body
当点击document时

点击document

在事件捕获的过程中,click事件首先在document上发生,然后沿DOM树向下传播,最后传播到child上。如果只点击parent,就从document沿着DOM树向下传播,直到自己被点击。

捕获过程

可以根据addEventListener()接收的第三个参数来规定是否选择事件冒泡或者事件捕获,true表示遵循事件捕获,false或者不传入参数表示遵循事件冒泡,建议使用事件冒泡,特殊情况下再使用事件捕获。

DOM2事件流

DOM2事件流包含了三个阶段:事件冒泡阶段,处于目标阶段,事件捕获阶段,首先发生的是事件捕获,为截获事件提供了机会,然后是事件的目标阶段接收阶段,最后一个阶段是冒泡阶段,可以在这个阶段对事件作出相应,如图