粗淺的總結下事件流

什麼是事件流?以及爲何要有事件流?

簡單的說,事件流就是,確認觸發條件知足時,事件對應函數的調用順序。舉個例子,鼠標光標在某個按鈕上點擊了,按鈕又綁定了mousedown事件,那麼其對應的函數就會調用。而其實,光標落下的位置也在document、window的範圍內,或許還可能在其餘元素的盒模型內。若是這些元素也都綁定了mousedown事件,那麼哪一個元素的mousedown事件對應的函數先調用呢?這就須要確認一個發生的順序問題。html

具體的說,事件流分爲三個階段,即捕獲階段、目標(處理)階段、冒泡階段。網上隨便找了一張示意圖,以下:瀏覽器

 

其實這張圖以及網上不少說法(好比百度百科DOM事件流)都不是很嚴謹,漏掉了window對象。事件流應該是從window開始,在window對象結束(若是捕獲階段和冒泡階段,window都綁定了事件的話)。估計是window對象一般只在冒泡階段綁定load事件吧,不談也沒什麼影響。ide

 

一個演示事件流的例子

下圖中,在鼠標單擊金黃色span區域時,控制檯對應打印出日誌。函數

源代碼在這裏:url

 1 <!DOCTYPE html>
 2 <html id="html">
 3     <head>
 4         <meta charset="UTF-8">
 5         <title></title>
 6         <style>
 7             div{
 8                 width: 200px;
 9                 height: 200px;
10                 font-size: 30px;
11                 color: #fff;
12                 background: green;
13             }
14             span{
15                 display: block;
16                 width: 100px;
17                 height: 100px;
18                 background: goldenrod;
19                 color: #fff;
20                 font-size: 30px;
21             }
22             body{
23                 border: 1px solid #000;
24             }
25         </style>
26         <script>
27             
28             window.onload=function(){
29                 var div=document.getElementById("div");
30                 var span=document.getElementById("span");
31                 var html=document.getElementById("html");
32                 var body=document.getElementById("body");
33                 var head=document.getElementById("head");
34 
35                 body.addEventListener('click',function(){console.log('body冒泡')},false);
36                 html.addEventListener('click',function(){console.log('html冒泡')},false);
37                 window.addEventListener('click',function(){console.log('window冒泡')},false);
38                 document.addEventListener('click',function(){console.log('document冒泡')},false);
39                 div.addEventListener('click',function(){console.log('div冒泡')},false); 
40                 span.addEventListener('click',function(){console.log('span冒泡')},false);
41                 span.addEventListener('click',function(){console.log('span捕獲')},true);
42                 document.addEventListener('click',function(){console.log('document捕獲')},true);
43                 window.addEventListener('click',function(){console.log('window捕獲')},true);
44                 html.addEventListener('click',function(){console.log('html捕獲')},true);
45                 body.addEventListener('click',function(){console.log('body捕獲')},true);
46                 div.addEventListener('click',function(){console.log('div捕獲')},true);
47             };
48         </script>
49     </head>
50     <body id="body">
51         <div id="div">div
52             <span id="span">span</span>
53         </div>
54     </body>
55 </html>
View Code

結合圖片和代碼,能夠發現兩個問題:spa

一、代碼中故意將打印帶有「冒泡」字樣的語句寫在了前面,並且也沒有按「window捕獲->document捕獲->html捕獲->body捕獲->div捕獲->span捕獲->span冒泡->div冒泡->body冒泡->html冒泡->document冒泡->window冒泡」這樣的順序,可是控制檯中打印的日誌基本上與事件流的順序一致。3d

二、第二個問題,也就是控制檯中打印的日誌與事件流的順序不一致的地方,爲何「span冒泡」在「span捕獲」前面呢?由於,觸發的是span綁定的事件,那麼span綁定的事件就在目標階段觸發。觸發順按照代碼中書寫順序來。本例中,即日誌

 span.addEventListener('click',function(){console.log('span冒泡')},false); code

htm

 span.addEventListener('click',function(){console.log('span捕獲')},true); 前面。

 

注:

addEventListener綁定事件語法: 對象.addEventListener(事件名稱,事件綁定函數,布爾值)

布爾值取值說明:
* true 事件是在捕獲的階段發生的
* false 事件是在冒泡的階段發生的(缺省值)

 

阻止事件流

說到事件流,通常都要說下阻止事件流。咱們知道能夠經過兩種方法綁定事件,一種是用on,一種是上面提到的addEventListener。針對綁定事件的方式不一樣,阻止事件流的方法也不一樣。

狀況1:

用on給元素綁定事件,全部的瀏覽器都是同樣的,先觸發事件源對象,而後再往外層元素冒泡。因此,只須要阻止冒泡就能夠了。將event對象身上的cancelBubble的值設爲true便可。

來看一個例子:

圖中,點擊box3後,box三、box2身上的事件會發生。而後,中止在box2,也就是不會在向外冒泡了。因此box1身上的事件沒有發生。

關鍵代碼以下:

 1                 box1.onclick=function(ev){
 2                     console.log('box1點擊了');
 3                 };
 4                 box2.onclick=function(ev){
 5                     console.log('box2點擊了');
 6                     ev.cancelBubble=true;   //這裏阻止了冒泡
 7                 };
 8                 box3.onclick=function(ev){
 9                     console.log('box3點擊了');
10                 };

狀況2:

用addEventListener綁定事件,可指定事件發生在捕獲階段仍是在冒泡階段。在事件函數內調用event身上的stopPropagation()方法,能夠阻止事件流繼續蔓延。

看一個具體的例子:

圖中,由於在冒泡階段box6綁定的事件函數內,阻止了事件流,點擊box6,事件流會在冒泡階段box6綁定的事件發生後中止。若是沒有阻止事件流,那麼控制檯中將會依次打印:捕獲階段box4點擊了->捕獲階段box5點擊了->捕獲階段box6點擊了->冒泡階段box4點擊了->冒泡階段box5點擊了->冒泡階段box6點擊了。

 

關鍵代碼以下:

 1                 box4.addEventListener('click',function(ev){
 2                     console.log('捕獲階段box4點擊了');
 3                 },true);
 4                 box5.addEventListener('click',function(ev){
 5                     console.log('捕獲階段box5點擊了');
 6                 },true);
 7                 box6.addEventListener('click',function(ev){
 8                     console.log('捕獲階段box6點擊了');
 9                 },true);
10                 
11                 box4.addEventListener('click',function(ev){
12                     console.log('冒泡階段box4點擊了');
13                 });
14                 box5.addEventListener('click',function(ev){
15                     console.log('冒泡階段box5點擊了');
16                 });                
17                 box6.addEventListener('click',function(ev){
18                     console.log('冒泡階段box6點擊了');
19                     ev.stopPropagation(); //由於在此處阻止了事件流蔓延,單擊box6的時候,只打印'冒泡階段box6點擊了'。不會波及到box5和box4。
20                 });    

 

 

知識共享許可協議
本做品採用知識共享署名 4.0 國際許可協議進行許可。

相關文章
相關標籤/搜索