思考
当我们给元素绑定好单击事件,单击这个元素,就会执行相应的代码,但是如果这个元素的父元素、祖先元素都绑定了单击事件,他们会执行吗?他们的执行顺序是什么呢?
可以尝试把页面理解为一个二维的平面,想象有一张白纸,我们在这张纸上画下了一层一层的同心圆,当我们用手指按住最内的圆圈时,也按住了纸上所有的同心圆,也按住了整张纸。所以单击事件不仅仅发生在被击中的元素上,换句话说,当我们用鼠标单击其中一个元素时,你也是在单击元素的容器,甚至一层一层向外传递(也可以是向内),甚至是在单击整个页面。
事件流定义简述
定义:描述的是从页面中接收事件的顺序
事件冒泡
IE的事件流叫做事件冒泡,即事件开始时有最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点,直到document对象。
下图展示了事件冒泡的过程
事件捕获
Netscape Communicator团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
下图展示了事件捕获的过程
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获、处于目标阶段和事件冒泡阶段。发生的顺序是事件捕获阶段==>目标阶段==>事件冒泡阶段
事件流执行测试
了解了基本概念,来用代码测试一下。
事件在冒泡阶段执行
html
<div id="first">
<div id="second">
<div id="third"></div>
</div>
</div>
css
#first{
width:200px;
height:200px;
margin:0 auto;
background:red;
border:1px solid #ccc;
}
#second{
width:150px;
height:150px;
margin:24px auto;
background:yellow;
border:1px solid #ccc;
}
#third{
width:100px;
height:100px;
margin:24px auto;
background:blue;
border:1px solid #ccc;
}
js
//第三个参数设为false,将事件处理程序添加到冒泡阶段
first.addEventListener('click',function(e){
var target=e.target;
console.log('红色框');
},false);
second.addEventListener('click',function(e){
var target=e.target;
console.log('黄色框');
},false);
third.addEventListener('click',function(e){
var target=e.target;
console.log('蓝色框');
},false);
单击嵌套最深的元素,控制台输出结果:
因为最内层元素本身有一个单击事件,它的父元素及祖先元素分别有一个单击事件,所以单击最内层元素位置,同时单击了三个元素,启动了三次事件处理程序。因为三个事件都是冒泡阶段执行,所以控制台输出的顺序为最内层的蓝色框——>中间的黄色框——>外层的红色框。
事件在捕获阶段执行
将第三个参数都改为true,控制台输出如下:
这时事件执行顺序改为从最外层的元素——>中间元素———>最内层的元素
我们可以很直观的看到事件同时都在冒泡阶段或捕获阶段执行的顺序,但如果这三个元素的事件处理程序在不在同一阶段执行呢?在看到结果之前可以试试预想一下它们的执行顺序。
事件在不同阶段执行
我们可以试一试将红色框与蓝色框的第三个参数设为false,黄色框的参数设为true。输出结果如下:
这和之前你预想的相同吗?我们可以这么来理解它们的执行:
单击蓝色框后,开始事件捕获阶段:
从最外层的document对象(浏览器其实是从window对象开始的)向内捕获事件,路过红色框时,查看到红色框有事件,但是红色框说:“我是在冒泡阶段执行,现在是捕获阶段,等你回来再说吧。”接下来是黄色框:“我在捕获阶段执行,就是现在执行!在控制台输“黄色框”吧~~”
接下来到达目标阶段:
“DOM2级事件”规范要求捕获阶段不会涉及事件目标即我们点击的那个最具体的元素,但IE9、Chrome等浏览器都会在捕获阶段触发事件对象上的事件。执行目标对象的事件函数,控制台输出“蓝色框”。
最后是冒泡阶段:
由目标对象向外传递,到达黄色框,黄色框说:“我在捕获阶段执行过了,你走吧...”然后到达红色框,红色框说:“你终于回来了,现在就执行我的事件!”控制台输出“红色框”。然后继续向外传播,直到到达document对象后停止。
其他:更改了元素绑定事件代码的顺序,执行顺序也和上面表现的一致。
总结
将事件添加到什么阶段执行,事件就会在触发事件后,在DOM事件流中相应的阶段执行。
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大程度的兼容各种浏览器(事件冒泡由IE提出),最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。
一个元素绑定事件时需要考虑到父元素及祖先元素的事件,应该注意它们之间的事件执行顺序及执行结果,所以推荐在合适的情况下使用事件代理。