关于DOM2级事件的事件捕获和事件冒泡

DOM2级事件中addEventListener的执行机制,多个addEventListener同时添加时的执行先后规律:

W3C的DOM事件触发分为三个阶段:
①、事件捕获阶段,即由最顶层元素(一般是从window元素开始,有的浏览器是从document开始,至于其中的差别我稍后会更新)开始,逐次进入dom内部,最后到达目标元素,依次执行绑定在其上的事件
②、处于目标阶段,检测机制到达目标元素,按事件注册顺序执行绑定在目标元素上的事件。
③、事件冒泡阶段,从目标元素出发,向外层元素冒泡,最后到达顶层(window或document),依次执行绑定再其上的事件。

在addEventListener中,利用第三个参数控制其是从哪个阶段开始,“true”是从捕获阶段开始,而“false”则是跳过捕获阶段,从冒泡阶段开始。 看一个简单的例子:

 1 <script type="text/javascript">
 2 window.onload=function(){
 3     var outer = document.getElementById("outer");
 4     var inner = document.getElementById('inner');
 5 
 6 outer.addEventListener("click",   function(){
 7       alert("1");
 8     }
 9 , true);
10 
11  inner.addEventListener("click",      function(){
12       alert("2");
13      }
14 , true);
15 
16  outer.addEventListener("click",
17      function(){
18         alert("3");
19     }
20 ,false)   
21 }
22 </script>
23 
24 <body>
25     <div id="outer">
26         <div id="inner"></div>
27     </div>
28 </body>

  在这个例子里,如果我们点击内层元素inner,那么处于捕获阶段的1最先弹出,接下来是目标元素2弹出,最后是处于冒泡阶段的3弹出,即:1,2,3. 即使在代码里变换三个绑定事件的顺序,只要点击的是inner,这个执行顺序就不会变。

那么问题来了,如果点击的是外层outer的话呢?

要明白这个问题,我们必须明确一点:目标事件在哪一层,事件流就在哪一层回流,即使在outer事件下还有许多子孙节点,事件流都不会在outer之后往内流,此时,inner上的事件不会被触发,因此在这段代码中,只会弹出1和3。

那么这两个数字哪个先弹呢?由于此时事件处于第二阶段,即“处于目标阶段”,弹出顺序取决的也不再是捕获或冒泡,而是谁在代码中先注册,因此,在这段代码中,弹出的是:1→3.

综上所述,事件在DOM中的执行顺序为:外层捕获事件→内层捕获事件→先注册的目标事件→后注册的目标事件→内层冒泡事件→外层冒泡事件

让我们再看个例子来加深这个概念:

 1 <script type="text/javascript">
 2 window.onload=function(){
 3     var outer = document.getElementById("outer");
 4     var inner = document.getElementById('inner');
 5     var oBox=document.getElementById('box');
 6     
 7     oBox.addEventListener("click",function(){
 8         alert('1');
 9     },true)
10     oBox.addEventListener("click",function(){
11         alert('4');
12     },false)
13     
14 
15     outer.addEventListener("click", function(){
16       alert("2");
17     }, true);
18 
19     
20     inner.addEventListener("click",function(){
21         alert('3.2')
22     },false)
23     
24 
25     inner.addEventListener("click", function(){
26       alert("3.1");
27      }, true);
28     
29 }
30 </script>
31 
32 <body>
33     <div id="box">
34         <div id="outer">
35             <div id="inner"></div>
36         </div>
37     </div>
38 </body>

 

在这段代码里,box上的捕获事件最先执行,然后是outer上的捕获事件,然后是inner上先注册的事件,然后是inner上后注册的事件,最后是box上的冒泡事件 弹出顺序为:1→2→3.2→3.1→4

补充一点,在ie8-中,由于addEventLister不起作用,我们使用attachEvent方法来绑定事件,此时在第二阶段,也就是处于目标阶段,如果目标元素上绑定了两个事件,那么其执行顺序和addEventLister相反:谁后注册谁先执行.

 

接下来我们将问题再升级,如果dom0级事件和dom2级事件同时存在,那执行顺序会是怎样呢?

看下面的事例代码:

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title>dom2</title>
 6     <style>
 7         #box{
 8             width:500px;
 9             height:500px;
10             background-color: red;
11         }
12 
13         #outer{
14             width:200px;
15             height:200px;
16             background-color: green;
17         }
18         #inner{
19             width:100px;
20             height:100px;
21             background-color: yellow;
22         }
23     </style>
24 </head>
25 <body>
26     <div id="box">
27         <div id="outer">
28             <div id="inner"></div>
29         </div>
30     </div>
31 </body>
32 
33 <script type="text/javascript">
34     window.onload = function () {
35 
36         var outer = document.getElementById("outer");
37         var inner = document.getElementById('inner');
38         var oBox = document.getElementById('box');
39 
40         oBox.addEventListener("click", function () {
41             alert('oBox-Dom2冒泡');
42         }, false);
43 
44         oBox.addEventListener("click", function () {
45             alert('oBox-Dom2捕获');
46         }, true);
47 
48         outer.addEventListener("click", function () {
49             alert("outer-Dom2冒泡");
50         }, false);
51 
52         outer.addEventListener("click", function () {
53             alert("outer-Dom2捕获");
54         }, true);
55 
56         outer.onclick=function(){
57             alert("outer-dom0 click! ");
58         }
59 
60         inner.addEventListener("click", function () {
61             alert('inner-Dom2冒泡');
62         }, false);
63 
64         inner.addEventListener("click", function () {
65             alert("inner-Dom2捕获");
66         }, true);
67     }
68 </script>

此案例中,我将outer的div在捕获阶段和冒泡阶段都绑定了点击事件,同时还在绑定了dom0级的点击事件处理函数,这时如果我们点击inner,我们会发现,事件的执行顺序是这样的  oBox-Dom2捕获--> outer-Dom2捕获 --> inner-Dom2冒泡 -->  inner-Dom2捕获 --> outer-Dom2冒泡 --> outer-dom0 click! --> oBox-Dom2冒泡 由此我们可以得出一个结论:当绑定dom0级事件元素不是目标元素时,那么绑定dom0级事件的处理是在冒泡阶段处理并按事件注册的先后顺序执行(W3C先注册的先执行) ,如果绑定dom0级事件的元素是目标元素时,则不论是捕获阶段绑定的处理函数还是冒泡阶段绑定的处理函数以及dom0级事件处理函数,他们的执行顺序都是按照注册的顺序执行(W3C先注册的先执行) 。

 

posted @ 2017-04-09 19:31 惠远建 阅读(...) 评论(...) 编辑 收藏