你了解事件流吗?

事件冒泡:即由文档中嵌套层次最深的元素接收,逐级向上传播(FF、chrome、opera、safari以及ie9以上都会一直传播至window,而ie9以下的则只冒泡到html)。
事件捕获:即由不太具体的元素最先接收事件,而最具体的元素安排在最后,逐级向下传播。
事件流的3个阶段:事件捕获阶段、目标阶段、事件冒泡阶段,虽然规定由document最先接收,但实际上都从window开始。在addEventListener中的this始终指的都是调用他的元素。attachEvent事件处理程序中调用的this始终指向window。
addEventListener把第三个参数设置为true,让他在事件捕获阶段触发事件处理程序,相反的,如果为false则在事件冒泡阶段触发,从而达到和事件冒泡一致的效果。
为了兼容IE提供了attachEvent(),这个方法只针对IE,因为IE8没有事件流,所以用这个方法添加的事件处理程序都被添加到冒泡阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
oDiv1.addEventListener('click',function(){
console.log('父元素冒泡');
},false);
oDiv1.addEventListener('click',function(){
console.log('父元素捕获');
},true);
oDiv2.addEventListener('click',function(){
console.log('子元素捕获');
},true);
oDiv2.addEventListener('click',function(){
console.log('子元素冒泡');
},false);
oDiv3.addEventListener('click',function(){
console.log('孙元素捕获');
},true);
oDiv3.addEventListener('click',function(){
console.log('孙元素冒泡');
},false);


下面是控制台的输出,一切都显得那么自然。

1
2
3
4
5
6
父元素捕获
子元素捕获
孙元素捕获
孙元素冒泡
子元素冒泡
父元素冒泡

当我们把孙元素的事件处理程序交换位置后….

1
2
3
4
5
6
oDiv3.addEventListener('click',function(){
console.log('孙元素冒泡');
},false);
oDiv3.addEventListener('click',function(){
console.log('孙元素捕获');
},true);

控制台的输出如下

1
2
3
4
5
6
父元素捕获
子元素捕获
孙元素冒泡
孙元素捕获
子元素冒泡
父元素冒泡

如果你给代码最深处再嵌套一个div,你会发现冒泡和捕获位置错乱总是出现在最深一层。
还记得事件流包括3个阶段吗?事件捕获阶段、目标阶段、事件冒泡阶段。
点击孙元素时处于目标阶段然后是冒泡,冒泡之后才触发捕获(这跟事件顺序有关),接着再往上冒,这一点可以用event的eventPhase属性去验证。