DOM事件標準定義了兩種事件流,這兩種事件流有著顯著的不同并且可能對你的應用有著相當大的影響。這兩種事件流分別是捕獲和冒泡。和許多Web技術一樣,在它們成為標準之前,Netscape和微軟各自不同地實現了它們。Netscape選擇實現了捕獲事件流,微軟則實現了冒泡事件流。幸運的是,W3C決定組合使用這兩種方法,并且大多數新瀏覽器都遵循這兩種事件流方式。
默認情況下,事件使用冒泡事件流,不使用捕獲事件流。然而,在Firefox和Safari里,你可以顯式的指定使用捕獲事件流,方法是在注冊事件時傳入useCapture參數,將這個參數設為true。下面用個例子分別來測試這兩種事件流。
1、冒泡事件流
當事件在某一DOM元素被觸發時,例如用戶在客戶名字節點上點擊鼠標,事件將跟隨著該節點繼承自的各個父節點冒泡穿過整個的DOM節點層次,直到它遇到依附有該事件類型處理器的節點,此時,該事件是onclick事件。在冒泡過程中的任何時候都可以終止事件的冒泡,在遵從W3C標準的瀏覽器里可以通過調用事件對象上的stopPropagation()方法,在Internet Explorer里可以通過設置事件對象的cancelBubble屬性為true。如果不停止事件的傳播,事件將一直通過DOM冒泡直至到達文檔根。
測試的HTML文件,其中用到了mootools-release-1.11.js,對mootools的代碼進行了改動:
addListener: function(type, fn,setCapture){
if (this.addEventListener) this.addEventListener(type, fn, setCapture);
else {
this.attachEvent('on' + type, fn);
if (setCapture) this.setCapture(true);
}
return this;
}
if (this.addEventListener) this.addEventListener(type, fn, setCapture);
else {
this.attachEvent('on' + type, fn);
if (setCapture) this.setCapture(true);
}
return this;
}
給addListener方法里增加了setCapture參數,用于測試捕獲事件流。
<body>
<div id="dd1-ct" style="width:400px;height:400px;border:1px solid #999;padding:2px">Container
<div id="dd1-item1" style="width:200px;height:200px;border:1px solid #999;padding:2px">Item1
<div id="dd1-item2" style="width:100px;height:100px;border:1px solid #999;padding:2px">Item2</div>
</div>
</div>
<div id='rh'></div>
</body>
<div id="dd1-ct" style="width:400px;height:400px;border:1px solid #999;padding:2px">Container
<div id="dd1-item1" style="width:200px;height:200px;border:1px solid #999;padding:2px">Item1
<div id="dd1-item2" style="width:100px;height:100px;border:1px solid #999;padding:2px">Item2</div>
</div>
</div>
<div id='rh'></div>
</body>
效果:

js:
fn1=function(e){
// e.stopPropagation();
$('rh').innerHTML+='Item1 clicked!******';
};
fn2=function(e){
// e.stopPropagation();
$('rh').innerHTML+='Item2 clicked!-------';
};
fn=function(e){
// e.stopPropagation();
$('rh').innerHTML+='Container clicked!&&&&&&&&';
};
$('dd1-item2').addListener('click', fn2.bindWithEvent(),false);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
// e.stopPropagation();
$('rh').innerHTML+='Item1 clicked!******';
};
fn2=function(e){
// e.stopPropagation();
$('rh').innerHTML+='Item2 clicked!-------';
};
fn=function(e){
// e.stopPropagation();
$('rh').innerHTML+='Container clicked!&&&&&&&&';
};
$('dd1-item2').addListener('click', fn2.bindWithEvent(),false);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
測試結果ie和ff下效果一致:單擊item2,會依次觸發fn2、fn1、fn;單擊item1,會依次觸發fn1、fn;單擊Container,只會觸發fn;當在任何一個事件處理器里調用e.stopPropagation();都會阻止事件的冒泡。
2、捕獲事件流
事件的處理將從DOM層次的根開始,而不是從觸發事件的目標元素開始,事件被從目標元素的所有祖先元素依次往下傳遞。在這個過程中,事件會被從文檔根到事件目標元素之間各個繼承派生的元素所捕獲,如果事件監聽器在被注冊時設置了useCapture屬性為true,那么它們可以被分派給這期間的任何元素以對事件做出處理;否則,事件會被接著傳遞給派生元素路徑上的下一元素,直至目標元素。事件到達目標元素后,它會接著通過DOM節點再進行冒泡。
這里ie與ff存在著很大的差異,甚至ie6與ie7的表現也各不相同,所以分開測試。
a、ff
事件從從DOM層次的根開始往下傳遞時,會被useCapture屬性為true的事件監聽器所捕獲,而到達目標元素再從目標元素冒泡時,則會被useCapture屬性為false的事件監聽器所捕獲。當在任何一個事件處理器里調用e.stopPropagation();都會阻止事件的傳播。
b、ie6
用事實說話:
第一種情況:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1-ct').addListener('click', fn.bindWithEvent(),true);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1-ct').addListener('click', fn.bindWithEvent(),true);
單擊瀏覽器的任何位置,都只是觸發fn;
第二種情況:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
單擊瀏覽器的任何位置,會依次觸發fn1、fn;
第三種情況:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
$('dd1-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1-ct').addListener('click', fn.bindWithEvent(),false);
單擊瀏覽器的任何位置,會依次觸發fn2、fn1、fn;
結論:如果HTML元素捕獲了通過該元素的setCapture()方法對這個元素的設置,依附于該元素的處理器將會被事件觸發,即使setCapture()方法
被調用的這個元素不在目標元素的祖先路徑中。事實上你甚至單擊瀏覽器的非頁面部分都會觸發事件處理器。并且事件一旦被捕獲就不會繼續再
往下傳播(即使該元素在目標元素的祖先路徑中),而是立刻冒泡。e.stopPropagation();會阻止事件的冒泡。
c、ie7
測試效果與冒泡事件流一致。將對捕獲事件流的支持干掉了?
結論:正如mootools所做的,避免捕獲事件流。
http://www.aygfsteel.com/ronghao 榮浩原創,轉載請注明出處:)