這個問題的整理是基于面試題:
給同一個DOM元素綁定兩個事件,一個用冒泡,一個用捕獲,會執(zhí)行幾次?先執(zhí)行冒泡還是先執(zhí)行捕獲?
例子:
<div id="outer">
<p id="inner">Click me!</p>
</div>
事件冒泡:
微軟提出了名為事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻為把一顆石頭投入水中,泡泡會一直從水底冒出水面。也就是說,事件會從最內(nèi)層的元素開始發(fā)生,一直向上傳播,直到document對象。
因此上面的例子在事件冒泡的概念下發(fā)生click事件的順序應(yīng)該是p -> div -> body -> html -> document
事件捕獲:
網(wǎng)景提出另一種事件流名為事件捕獲(event capturing)。與事件冒泡相反,事件會從最外層開始發(fā)生,直到最具體的元素。
上面的例子在事件捕獲的概念下發(fā)生click事件的順序應(yīng)該是document -> html -> body -> div -> p
如下代碼,有四個div嵌套元素,均綁定了click事件,addEventListener函數(shù)的第三個參數(shù)設(shè)置為false說明不為捕獲事件,即為冒泡事件。該代碼執(zhí)行結(jié)果如下:
點(diǎn)擊one元素,輸出one;
點(diǎn)擊two元素,輸出two one;
點(diǎn)擊three元素,輸出 three two one;
點(diǎn)擊four元素,輸出 four three two one;
<div id='one'>
<div id='two'>
<div id='three'>
<div id='four'>
</div>
</div>
</div>
</div>
<script>
var one=document.getElementById('one');
var two=document.getElementById('two');
var three=document.getElementById('three');
var four=document.getElementById('four');
one.addEventListener('click',function(){
alert('one');
},false);
two.addEventListener('click',function(){
alert('two');
},false);
three.addEventListener('click',function(){
alert('three');
},false);
four.addEventListener('click',function(){
alert('four');
},false);
</script>
而捕獲則相反。當(dāng)觸發(fā)目標(biāo)元素時,會從目標(biāo)元素的最頂層的祖先元素事件往下執(zhí)行到目標(biāo)元素為止。
將上面的代碼第三個參數(shù)均改為true,則執(zhí)行結(jié)果如下:
點(diǎn)擊one,輸出one;
點(diǎn)擊two,輸出one two;
點(diǎn)擊three,輸出one two three;
點(diǎn)擊four,輸出one two three four;
很明顯執(zhí)行順序是不同的。
問題解釋:
無論是冒泡事件還是捕獲事件,元素都會先執(zhí)行捕獲階段
從上往下,如有捕獲事件,則執(zhí)行;一直向下到目標(biāo)元素后,從目標(biāo)元素開始向上執(zhí)行冒泡元素
如下代碼:
此時點(diǎn)擊four元素,four元素為目標(biāo)元素,one為根元素祖先,從one開始向下判斷執(zhí)行。
one為捕獲事件,輸出one;
two為冒泡事件,忽略;
three為捕獲時間,輸出three;
four為目標(biāo)元素,開始向上冒泡執(zhí)行,輸出four;(從此處分為兩部分理解較容易。)
three為捕獲已執(zhí)行,忽略;
two為冒泡事件,輸出two;
one為捕獲已執(zhí)行,忽略。
最終執(zhí)行結(jié)果為:one three four two
one.addEventListener('click',function(){
alert('one');
},true);
two.addEventListener('click',function(){
alert('two');
},false);
three.addEventListener('click',function(){
alert('three');
},true);
four.addEventListener('click',function(){
alert('four');
},false);
執(zhí)行次數(shù):綁定了幾個事件便執(zhí)行幾次。
如下代碼,two元素綁定了兩個不同事件,點(diǎn)擊two都會執(zhí)行這兩個事件。而執(zhí)行順序有所差異
1、如果two為目標(biāo)元素,目標(biāo)元素的事情按順序執(zhí)行,而其他元素根據(jù)W3C的標(biāo)準(zhǔn)執(zhí)行,即先捕獲后冒泡。
點(diǎn)擊two執(zhí)行結(jié)果:one(因?yàn)槭莟wo的父元素支持捕獲事件所以先執(zhí)行) two,bubble two,capture(順序執(zhí)行,注意逗號不是間隔,是輸出內(nèi)容。)
2、如果目標(biāo)元素不是two,則two的兩個事件按先捕獲后冒泡觸發(fā)執(zhí)行,也就是跟前面討論的執(zhí)行過程是一樣的,只不過兩個事件都綁定在同一個DOM元素上。
點(diǎn)擊three執(zhí)行結(jié)果:one two,capture three,capture two,bubble
one.addEventListener('click',function(){
alert('one');
},true);
two.addEventListener('click',function(){
alert('two,bubble');
},false);
two.addEventListener('click',function(){
alert('two,capture');
},true);
three.addEventListener('click',function(){
alert('three,capture');
},true);
four.addEventListener('click',function(){
alert('four,capture');
},true);