原文: http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/#
?
傳統的事件處理
所謂事件委托即使用單個Event Handler來管理頁面上特定的一類事件。這并不是什么新的idea,但對于Web應用的性能而言,是很重要的。比如,有時候,你可能會寫如下的代碼:
document.getElementById("help-btn").onclick = function(event){ openHelp(); }; document.getElementById("save-btn").onclick = function(event){ saveDocument(); }; document.getElementById("undo-btn").onclick = function(event){ undoChanges(); };
它為頁面上每一個可交互的元素提供了一個Event Handler。在一個小型的Web應用中,這可能問題不明顯。但如果這是一個大型的網站,存在大量的交互,這么多的handler可能就會出現性能問題了。可以想象,為了處理數以百計的交互,我們需要將大量的DOM元素和時間處理代碼綁定起來。這會占用大量的內存,進而導致整個應用運行變慢。而事件委托就可以用來解決這個問題。
事件冒泡和捕獲(Event Bubbling / Capturing)
在早期Web開發的年代,瀏覽器廠商需要考慮一個問題:當用戶在Web頁面上點擊一個區域,如何判斷他實際正在進行互交互的元素。與之相關的是我們如何定義交互。在一個元素內點擊的操作是有二義性的。如下圖所示,如果用戶點擊了一個button元素,這個交互發生在button內部,同時我們也可以說它發生在body元素的內部,同時還在html元素的內部。
????? 在這個問題被提出來的時候,當時的兩個主要瀏覽器(Netscape Navigator 和 Internet Explorer)采取了不同方式來解決這個問題。Netscape使用了一種叫做事件捕捉的機制,事件首先發生在DOM樹上最高層的元素上,然后逐層往下傳遞到最深的被影響的元素上。所以,在上述例子中,點擊事件首先由document元素來處理,然后是html元素,接下來是body,最后才是button元素。
????? Internet Explorer正好使用了完全相反的方式。他們稱之為事件冒泡:最底層的元素應該首先接受到這個時間,然后才是它的父節點,祖父節點……一直到最高層的document元素。值得注意的是,雖然document和<html>元素對應的是頁面上同一個可視元素,但它們在DOM樹上卻是父子關系。Event Bubbling的終點是document元素。
在定義DOM的時候,W3C顯然注意到了兩種不同方式各自的好處,因此我們現在使用的DOM Level 2事件機制中同時包含了這兩種方式。一開始document元素接收到這個事件,進入捕獲階段,將其傳遞到最底層的元素。在這個元素的處理邏輯完成以后,進入冒泡階段,將其傳遞回到document。在DOM Level2事件API中,addEventListener方法接受三個參數:要處理的事件名,事件處理函數,一個bool值(true表示在捕獲階段處理事件,而false表示在冒泡階段處理事件)。多數Web開發人員經常會被告知使用false作為參數值,以保持和IE中的attachEvent方法相同的行為。比如:
//bubbling phase handler document.addEventListener("click", handleClick, false); //capturing phase handler document.addEventListener("click", handleClick, true);
使用形如
element.onclick = function(){}
的方式來綁定事件的方式,即DOM Level 1的事件機制,是在冒泡階段處理事件的(向前兼容性)。大多數瀏覽器(除了IE)都支持DOM Level 2事件機制,也就是說同時支持事件捕獲和冒泡。IE仍然使用的是其專有的,僅支持冒泡方式的事件處理機制。
?
事件委托的關鍵就是使用事件冒泡的特性在DOM最高層的元素來處理它們(通常是document)。當然,不是所有的事件都支持冒泡(如onsubmit,onfocus,onblur這一類不和輸入設備直接相關的時間等),但是鼠標和鍵盤事件都可以(包括onclick)。而且,很幸運的是,這些通常是你會感興趣的。重新考慮之前的例子,你可以在document的事件處理函數中,檢查事件的目標元素,并完成相應的邏輯。
document.onclick = function(event){ //IE doesn't pass in the event object event = event || window.event; //IE uses srcElement as the target var target = event.target || event.srcElement; switch(target.id){ case "help-btn": openHelp(); break; case "save-btn": saveDocument(); break; case "undo-btn": undoChanges(); break; //others? } };
事件委托可以讓同一事件的處理入口都匯集到一個函數中。所有的click事件現在都會先被單個函數所處理,并根據時間的對象元素委托給合適的函數。同樣的,我們把它應用于
mousedown
,
mouseup
,
mousemove
,
mouseover
,
mouseout
,
dblclick
,
keyup
,
keydown
, 和
keypress
事件. 這里要注意的一點就是,
由于mouseover
和mouseout事件的特性(僅當鼠標移出容器時才會觸發
mouseout),事件代理對它們來說并不是很實用。
當然,你也可以通過事件捕獲來完成Event delegation,但這僅在支持事件捕獲的瀏覽器中有效,也就是說IE不支持該方式。
好處
對于一個web應用來說,事件代理具有以下幾點好處:
- 較少的事件處理函數.
- 占用更少的內存.
- 減少了Javascript和DOM之間的耦合.
-
在改變元素的innerHTML時,不用去移除綁定事件處理函數
.
相比于傳統的事件處理方式,事件委托提高了大型web應用的總體性能。它對于Javascript庫至關重要,如 YUI 和 jQuery 已經使用了這種機制。實現事件委托并不難,但是在用戶界面的性能上的提高確實非常顯著的。這在你將大量的事件處理函數合并為一個的時候,尤其明顯。試試事件委托機制吧!
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
