讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議118:使用DOM樹結構托管事件 >

建議118:使用DOM樹結構托管事件

當頁面中存在大量元素,並且每個元素有一個或多個事件句柄連接(如onclick)時,可能會影響性能。連接每個句柄都是有代價的,這代價可能是加重了頁面負擔(更多的頁面標記和JavaScript代碼),也可能表現在運行期的運行時間上。要訪問和修改更多的DOM節點,程序就會更慢,特別是事件連接過程都發生在onload(或DOMContentReady)事件中時,對任何一個富交互網頁來說這都是一個繁忙的時間段。連接事件佔用了處理時間,另外,瀏覽器需要保存每個句柄的記錄,也會佔用更多內存。當這些工作結束時,由於這些事件句柄中的相當一部分根本不需要(因為並不是百分之百的按鈕或鏈接都會被用戶單擊到),所以很多工作都是不必要的。

一個簡單而優雅的處理DOM事件的技術是事件托管。它基於這樣一個事實:事件逐層冒泡總能被父元素捕獲。採用事件托管技術後,只需要在一個包裝元素上連接一個句柄,用於處理子元素發生的所有事件。根據DOM標準,每個事件有3個階段:

❑捕獲

❑到達目標

❑冒泡

IE不支持捕獲,只要實現托管技術使用冒泡就足夠了。


<p>

<ul>

<li><a urn="#1"></a></li>

</ul>

</p>


例如,對於上面的結構,當用戶單擊「menu#1」鏈接時,單擊事件首先被<a>元素收到,然後它沿著DOM樹冒泡,被<li>元素收到,之後被<ul>元素收到,接著是<p>等,一直到達文檔的頂層,甚至window對象。這使得可以只在父元素上連接一個事件句柄,以接收所有子元素產生的事件通知。

假設要為上面文檔結構提供一個逐步增強的Ajax體驗,用戶關閉了JavaScript,菜單中的鏈接仍然可以正常地重載頁面。當已經打開JavaScript且用戶代理有足夠能力時,如果希望截獲所有單擊,阻止默認行為(轉入鏈接),發送一個Ajax請求獲取內容,然後不刷新頁面就能夠更新部分頁面,那麼使用事件托管實現此功能,可以在menu單元連接一個單擊監聽器,它封裝所有鏈接並監聽所有click事件。


document.getElementById('menu').onclick=function(e){

e=e||window.event;

var target=e.target||e.srcElement;

var pageid,hrefparts;

if(target.nodeName!=='A'){

return;

}

hrefparts=target.href.split('/');

pageid=hrefparts[hrefparts.length-1];

pageid=pageid.replace('.html','');

ajaxRequest('xhr.php?page='+id,updatePageContents);

if(typeof e.preventDefault==='function'){

e.preventDefault;

e.stopPropagation;

}else{

e.returnValue=false;

e.cancelBubble=true;

}

};


事件托管技術並不複雜,只需要通過監聽事件偵測事件是不是從目標元素中發出的。這裡有一些冗余的跨瀏覽器代碼,如果將它們移入一個可重用的庫中,代碼就變得相當乾淨。跨瀏覽器部分包括:

❑訪問事件對象,並判斷事件源(目標)。

❑結束文檔樹上的冒泡(可選)。

❑阻止默認動作(可選,在本示例中是必須的,因為任務是捕獲這些鏈接而不轉入這些鏈接)。