談起JavaScript的 事件,事件冒泡、事件捕獲、阻止默認事件這三個話題,不管是面試仍是在平時的工做中,都很難避免。javascript
DOM事件標準定義了兩種事件流,這兩種事件流有着顯著的不一樣而且可能對你的應用有着至關大的影響。這兩種事件流分別是捕獲和冒泡。和許多Web技術同樣,在它們成爲標準以前,Netscape和微軟各自不一樣地實現了它們。Netscape選擇實現了捕獲事件流,微軟則實現了冒泡事件流。幸運的是,W3C決定組合使用這兩種方法,而且大多數新瀏覽器都遵循這兩種事件流方式。html
概念:java
事件冒泡:事件最開始由最具體的元素接收(文檔層次中嵌套最深的那個節點),而後逐級向上傳播最不具體的節點(document)。面試
事件捕獲:事件由最不具體的元素接收,最後傳播至最具體的元素。瀏覽器
事件冒泡代碼實例:安全
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>事件冒泡</title> </head> <body> <div id="e"> <input id="btn" type="button" value="按鈕"> </div> <script type="text/javascript"> var e = document.getElementById('e'); var btn = document.getElementById('btn'); e.onclick = function (){ alert("你點擊了div"); }; btn.onclick = function(){ alert("你點擊了button"); } </script> </body> </html>
當我點擊了按鈕時候,運行 --你點擊了button ——你點擊了div,這就是事件冒泡。函數
事件捕獲代碼:url
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>事件冒泡</title> </head> <body> <div id="e"> <input id="btn" type="button" value="按鈕"> </div> <script type="text/javascript"> var e = document.getElementById('e'); var btn = document.getElementById('btn'); e.addEventListener('click',function(){ alert('你點擊了div'); },true); btn.addEventListener('click',function(){ alert('你點擊了button'); },true); </script> </body> </html>
當我點擊了按鈕時候,運行 --你點擊了div ——你點擊了button,這就是事件捕獲。spa
(1)冒泡型事件:事件按照從最特定的事件目標到最不特定的事件目標(document對象)的順序觸發。.net
IE 5.5: input -> div -> body -> document
IE 6.0: input -> div -> body -> html -> document
Mozilla 1.0: input -> div -> body -> html -> document -> window
事件冒泡見下圖:
(2)捕獲型事件(event capturing):事件從最不精確的對象(document 對象)開始觸發,而後到最精確(也能夠在窗口級別捕獲事件,不過必須由開發人員特別指定)。
事件捕獲見下圖:
(3)DOM事件流:同時支持兩種事件模型:捕獲型事件和冒泡型事件,可是,捕獲型事件先發生。兩種事件流會觸及DOM中的全部對象,從document對象開始,也在document對象結束。
通常來講事件冒泡機制,用的更多一些,因此在IE8以及以前,IE只支持事件冒泡。IE9+/FF/Chrome這2種模型都支持,能夠經過addEventListener((type, listener, useCapture)的useCapture來設定,useCapture=false表明着事件冒泡,useCapture=true表明着採用事件捕獲。而不兼容W3C的瀏覽器(IE)用attachEvent()方法,此方法沒有相關設置,不過IE的事件模型默認是在事件冒泡時執行的,也就是在useCapture等於false的時候執行,因此把在處理事件時把useCapture設置爲false是比較安全,也實現兼容瀏覽器的效果。
DOM事件流:將事件分爲三個階段:捕獲階段、目標階段、冒泡階段。先調用捕獲階段的處理函數,其次調用目標階段的處理函數,最後調用冒泡階段的處理函數。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目標(自身觸發事件,是冒泡仍是捕獲無所謂) outC.addEventListener('click',function(){alert("target");},true); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false); // 事件捕獲 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true); }; </script> <body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div> </body>
當點擊outC的時候,依次打印出capture1-->capture2-->target-->bubble2-->bubble1。到這裏是否是能夠理解addEventListener(type,handler,useCapture)這個API中第三個參數useCapture的含義呢?useCapture=false意味着:將事件處理函數加入到冒泡階段,在冒泡階段會被調用;useCapture=true意味着:將事件處理函數加入到捕獲階段,在捕獲階段會被調用。從DOM事件流模型能夠看出,捕獲階段的事件處理函數,必定比冒泡階段的事件處理函數先執行。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目標(自身觸發事件,是冒泡仍是捕獲無所謂) outC.addEventListener('click',function(){alert("target2");},true); outC.addEventListener('click',function(){alert("target1");},true); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false); // 事件捕獲 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true); }; </script> <body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div> </body>
點擊outC的時候,打印順序是:capture1-->capture2-->target2-->target1-->bubble2-->bubble1。因爲outC是咱們觸發事件的目標對象,在outC上註冊的事件處理函數,屬於DOM事件流中的目標階段。目標階段函數的執行順序:先註冊的先執行,後註冊的後執行。這就是上面咱們說的,在目標對象上綁定的函數是採用捕獲,仍是採用冒泡,都沒有什麼關係,由於冒泡和捕獲只是對父元素上的函數執行順序有影響,對本身沒有什麼影響。若是不信,能夠將下面的代碼放進去驗證。
此咱們能夠給出事件函數執行順序的結論了:捕獲階段的處理函數最早執行,其次是目標階段的處理函數,最後是冒泡階段的處理函數。目標階段的處理函數,先註冊的先執行,後註冊的後執行。
默認狀況下,多個事件處理函數會按照DOM事件流模型中的順序執行。若是子元素上發生某個事件,不須要執行父元素上註冊的事件處理函數,那麼咱們能夠中止捕獲和冒泡,避免沒有意義的函數調用。前面提到的5種事件綁定方式,均可以實現阻止事件的傳播。IE8以及之前可過 window.event.cancelBubble=true阻止事件的繼續傳播;IE9+/FF/Chrome經過event.stopPropagation()阻止事件的繼續傳播。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目標 outC.addEventListener('click',function(event){ alert("target"); if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true;//IE阻止事件冒泡,true表明阻止 } },false); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble");},false); // 事件捕獲 outA.addEventListener('click',function(){alert("capture");},true); }; </script> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div>
當點擊outC的時候,以後打印出capture-->target,不會打印出bubble。由於當事件傳播到outC上的處理函數時,經過stopPropagation阻止了事件的繼續傳播,因此不會繼續傳播到冒泡階段。
原文連接:http://lib.csdn.net/article/javascript/6265?knId=505