JavaScript事件冒泡
JavaSciprt事件中有兩個很重要的特性:事件冒泡以及目標元素。
當一個元素上的事件被觸發的時候,比如說鼠標點擊了一個按鈕,同樣的事件將會在那個元素的所有祖先元素中被觸發。這一過程被稱為事件冒泡;這個事件從原始元素開始一直冒泡到DOM樹的最上層。
任何一個事件的目標元素都是最開始的那個元素,在我們的這個例子中也就是按鈕,并且它在我們的元素對象中以屬性的形式出現。使用事件代理的話我們可以把事件處理器添加到一個元素上,等待一個事件從它的子級元素里冒泡上來,并且可以很方便地得知這個事件是從哪個元素開始的。
事件的冒泡和捕獲
捕獲是從上級元素到下級元素,冒泡是從下級元素到上級元素。
在IE中,每個元素和window對象都有兩個方法:attachEvent()和detachEvent()。attachEvent()用來給一個事件附加事件處理函數。而detachEvent()用來將事件處理函數分離出來。
var fnClick = function() { alert("Clicked!"); } var oDiv = document.getElementById("div1"); oDiv.attachEvent("onclick", fnClick); oDiv.detachEvent("onclick", fnClick);
事件的冒泡有什么好處
想象一下現在我們有一個10列、100行的HTML表格,你希望在用戶點擊表格中的某一單元格的時候做點什么。比如說我有一次就需要讓表格中的每一個單元格在被點擊的時候變成可編輯狀態。如果把事件處理器加到這1000個單元格會產生一個很大的性能問題,并且有可能導致內存泄露甚至是瀏覽器的崩潰。相反地,使用事件代理的話,你只需要把一個事件處理器添加到table元素上就可以了,這個函數可以把點擊事件給截下來,并且判斷出是哪個單元格被點擊了。
代碼很簡單,我們所要關心的只是如何檢測目標元素而已。比方說我們有一個 table元素,ID是“report”,我們為這個表格添加一個事件處理器以調用editCell函數。editCell函數需要判斷出傳到table 來的事件的目標元素。考慮到我們要寫的幾個函數中都有可能用到這一功能,所以我們把它單獨放到一個名為getEventTarget的函數中:
function getEventTarget(e) { e = e || window.event; return e.target || e.srcElement; }
e這個變量表示的是一個事件對象,我們只需要寫一點點跨瀏覽器的代碼來返回目標元素,在IE里目標元素放在srcElemtn屬性或event.toElement屬性中,而在其它瀏覽器里則是target或event.relatedTarget屬性。
接下來就是editCell函數了,這個函數調用到了 getEventTarget函數。一旦我們得到了目標元素之后,剩下的事情就是看看它是否是我們所需要的那個元素了。
function editCell(e) { var target = getEventTarget(e); if(target.tagName.toLowerCase() === 'td') { // DO SOMETHING WITH THE CELL } }
在editCell函數中,我們通過檢查目標元素標簽名稱的方法來確定它是否是一個表格的單元格。這種檢查也許過于簡單了點;如果它是這個目標元素單元格里的另一個元素呢?我們需要為代碼做一點小小的修改以便于其找出父級的td 元素。如果說有些單元格不需要被編輯怎么辦呢?此種情況下我們可以為那些不可編輯的單元格添加一個指定的樣式名稱,然后在把單元格變成可編輯狀態之前先檢查它是否不包含那個樣式名稱。選擇總是多樣化的,你只需找到適合你應用程序的那一種。
事件冒泡的優點和缺點
那些需要創建的以及駐留在內存中的事件處理器少了。這是很重要的一點,這樣我們就提高了性能,并降低了崩潰的風險。
在DOM更新后無須重新綁定事件處理器了。如果你的頁面是動態生成的,比如說通過Ajax,你不再需要在元素被載入或者卸載的時候來添加或者刪除事件處理器了。
潛在的問題也許并不那么明顯,但是一旦你注意到這些問題,你就可以輕松地避免它們:你的事件管理代碼有成為性能瓶頸的風險,所以盡量使它能夠短小精悍。
不是所有的事件都能冒泡
blur、focus、load和unload不能像其它事件一樣冒泡。事實上blur和focus可以用事件捕獲而非事件冒泡的方法獲得(在IE之外的其它瀏覽器中)。
需要注意的是:如果你的代碼處理mousemove事件的話你遇上性能瓶頸的風險可就大了,因為mousemove事件觸發非常頻繁。而mouseout則因為其怪異的表現而變得很難用事件代理來管理。
阻止事件冒泡
在IE下解決問題很簡單,用onMouseEnter、 onMouseLeave來代替onMouseOver、onMouseOut就行了,他們的作用基本相同,前者不會發生冒泡。但是 firefox下沒有這兩個事件.
window.event.cancelBubble = true (IE) event.stopPropagation() event.preventDefault() (Firefox)
在IE和FF瀏覽器內阻止冒泡行為是不同的。IE中使用cancelBubble,FF中使用stopPropation()。
<html> <head> <title>阻止冒泡</title> <style> body{ color:#333; font-size:12px; } </style> </head> <body> <p>什么是事件冒泡,通俗的來講就是 我們為頁面內body添加一個單擊事件,同樣再為頁面內li元素添加一個單擊事件..當你單擊li的時候.<br> body的事件也會被觸發.....因為li被包含在body元素內...你單擊了li同時也單擊了body...這就是事件冒泡....<br /> 在有些時候.我們要阻止這種情況發生...也就是單擊li事件.不會觸發body的事件.... 這就叫阻止冒泡! <div id="a"> <ul id="lia">請單擊下面的列表.會觸發body事件 <li>項目1</li> <li>項目2</li> <li>項目3</li> </ul> <ul id="lib">請單下面的列表.不會觸發body事件. <li>項目1</li> <li>項目2</li> <li>項目3</li> </ul> </div> <script language="javascript"> document.body.onclick = function(){//首先為body元素綁定一個單擊事件 alert("BODY事件");//單擊頁面即彈出對話框 } function att_Event(){//為第一組li元素綁定onclick事件 var li = document.getElementById("lia").getElementsByTagName("li"); for(var i=0;i<li.length;i++){ li[i].onclick = function(){ alert("Li事件"); } } } function att_Event_b(){//為第二組li元素綁定onclick事件 var li = document.getElementById("lib").getElementsByTagName("li"); for(var i=0;i<li.length;i++){ li[i].onclick = function(e){ alert("Li事件"); stopBubble(e); //運行阻止冒泡的函數 } } } //該函數的功能用來阻止事件冒泡.并兼容多瀏覽器 function stopBubble(e){ //如果傳入了事件對象.那么就是非IE瀏覽器 if(e && e.stopPropagation){ //因此它支持W3C的stopPropation()方法 e.stopPropagation(); } else { //否則,我們得使用IE的方式來取消事件冒泡 window.event.cancelBubble = true; } } window.onload = function(){ att_Event(); att_Event_b(); } </script> </body> </html>
阻止jQuery事件冒泡
jQuery對DOM的事件觸發具有冒泡特性。有時利用這一特性可以減少重復代碼,但有時候我們又不希望事件冒泡。這個時候就要阻止 jQuery.Event冒泡。
在jQuery.Event的文檔中的開頭得知,jQuery.Event對象是符合W3C標準的一個事件對象,同時jQuery.Event免去了檢查兼容IE的步驟。
jQuery.Event提供了一個非常簡單的方法來阻止事件冒泡:event.stopPropagation();
$("p").click(function(event){ event.stopPropagation(); // do something })
但是這個方法對使用live綁定的事件沒有作用,需要一個更簡單的方法阻止事件冒泡:return false;
$(this).live("click",function(){ "Another paragraph!"; return false; });
原文地址:http://www.nowamagic.net/javascript/js_EventBubbling.php