當使用事件捕獲時javascript
| | ---------------| |----------------- | outer | | | | -----------| |----------- | | |inner \ / | | | ------------------------- | | Event CAPTURING | -----------------------------------
outer
上的事件處理器先觸發, 而後是inner
上的java
/ \
---------------| |----------------- | outer | | | | -----------| |----------- | | |inner | | | | | ------------------------- | | Event BUBBLING | -----------------------------------
與事件捕獲相反, 當使用事件冒泡時, inner上的事件處理器先被觸發, 其後是outer上面的。node
通常來講事件冒泡機制,用的更多一些,因此在IE8以及以前,IE只支持事件冒泡。IE9+/FF/Chrome這2種模型都支持,能夠經過addEventListener((type, listener, useCapture)的useCapture來設定,useCapture=false表明着事件冒泡,useCapture=true表明着採用事件捕獲。默認是false,即事件冒泡。app
DOM事件流函數
DOM事件流:將事件分爲三個階段:捕獲階段、目標階段、冒泡階段。先調用捕獲階段的處理函數,其次調用目標階段的處理函數,最後調用冒泡階段的處理函數。ui
咱們在outC上觸發onclick事件(這個是目標對象),若是咱們在outC上同時綁定捕獲階段/冒泡階段事件處理函數會怎麼樣呢?this
<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事件流中的目標階段。目標階段函數的執行順序:先註冊的先執行,後註冊的後執行。這就是上面咱們說的,在目標對象上綁定的函數是採用捕獲,仍是採用冒泡,都沒有什麼關係,由於冒泡和捕獲只是對父元素上的函數執行順序有影響,對本身沒有什麼影響。若是不信,能夠將下面的代碼放進去驗證。spa
// 目標(自身觸發事件,是冒泡仍是捕獲無所謂) outC.addEventListener('click',function(){alert("target1");},false); outC.addEventListener('click',function(){alert("target2");},true); outC.addEventListener('click',function(){alert("target3");},true); outC.addEventListener('click',function(){alert("target4");},false);
至此咱們能夠給出事件函數執行順序的結論了:捕獲階段的處理函數最早執行,其次是目標階段的處理函數,最後是冒泡階段的處理函數。目標階段的處理函數,先註冊的先執行,後註冊的後執行。.net
1. 阻止冒泡事件代理
主要是用於阻止事件傳播。阻止它被分派到其餘的DOM節點上,在事件傳播的任何階段都能使用。使用方法以下(兼容IE):
function stopBubble(event){ if(window.event){//兼容IE window.event.cancelBubble=true; }else{ event.stopPropagation(); }
2. 阻止默認事件
像submit這類的表單元素,都會綁定默認事件,若是不阻止默認事件,則綁定的其餘方法也會無效。使用方法以下(兼容IE):
function stopDefaultEvent(event){ if(window.event){//兼容IE window.event.returnValue=false; }else{ event.preventDefault() } return false; }
上面的例子是要阻止冒泡,有時候冒泡機制也能夠被利用。先看一個問題:
假設要在頁面上放在 10^n(n>=1) 個列表項元素,當我點擊某個元素時,須要輸出點擊的是第幾個。
通常作法是,遍歷時給每一個元素綁定點擊事件:
var li = document.getElementsByTagName('li'); for(var i=0; i<li.length; i++){ li[i].setAttribute('i',i+1); li[i].addEventListener('click', function(e){ var b = this.getAttribute('i'); console.log('這是第' + b + '個<li>元素'); }); }
另外一種方法是,能夠利用冒泡機制,在父元素只綁定一次點擊事件:
var li = document.getElementsByTagName('li'); for(var i=0; i<li.length; i++){ li[i].setAttribute('i',i+1); } var ul = document.getElementById('wrapper'); ul.addEventListener('click', function(e){ if(e.target && e.target.nodeName.toUpperCase() === 'LI'){ var b = e.target.getAttribute('i'); console.log('這是第' + b + '個<li>元素'); } });
這種事件委託的方式減小了事件處理程序,也能下降程序的複雜性和出錯機率。
參考:
https://www.jb51.net/article/94394.htm
https://blog.csdn.net/michael8512/article/details/77447537