1. 程式人生 > >javascript的事件代理

javascript的事件代理

如果你想給網頁新增點JavaScript的互動性,也許你已經聽過JavaScript的事件代理event delegation),並且覺得這是那些發燒友級別的JavaScript程式設計師才會關心的什麼費解的設計模式之一。事實上,如果你已經知道怎麼新增JavaScript的事件處理器event handler),實現事件代理也是件輕而易舉的事情。

JavaScript事件是所有網頁互動性的根基(我指的是真正的互動性,而不僅是那些CSS下拉選單)。在傳統的事件處理中,你按照需要為每一個元素新增或者是刪除事件處理器。然而,事件處理器將有可能導致記憶體洩露或者是效能下降——你用得越多這種風險就越大。JavaScript事件代理

則是一種簡單的技巧,通過它你可以把事件處理器新增一個父級元素上,這樣就避免了把事件處理器新增到多個子級元素上。

它是怎麼運作的呢?

事件代理用到了兩個在JavaSciprt事件中常被忽略特性事件冒泡以及目標元素。當一個元素上的事件被觸發的時候,比如說滑鼠點選了一個按鈕,同樣的事件將會在那個元素的所有祖先元素被觸發。這一過程被稱為事件冒泡;這個事件從原始元素開始一直冒泡到DOM樹的最上層。任何一個事件的目標元素都是最開始的那個元素,在我們的這個例子中也就是按鈕,並且它在我們的元素物件中以屬性的形式出現。使用事件代理,我們可以把事件處理器新增一個元素上,等待一個事件它的子級元素冒泡上來

,並且可以得知這個事件是從哪個元素開始的。

這對我有什麼好處呢?

想象一下現在我們有一個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裡目標元素放在srcElemnt屬性中,而在其它瀏覽器裡則是target屬性。

接下來就是editCell函數了,這個函式呼叫到了getEventTarget函式。一旦我們得到了目標元素,剩下的事情就是看看它是否是我們所需要的那個元素了。

function editCell(e)

{
var target = getEventTarget(e);
if(target.tagName.toLowerCase() =='td')

{
// DO SOMETHING WITH THE CELL
}
}

在editCell函式中,我們通過檢查目標元素標籤名稱的方法來確定它是否是一個表格的單元格。這種檢查也許過於簡單了點;如果它是這個目標元素單元格里的另一個元素呢?我們需要為程式碼做一點小小的修改以便於其找出父級的td元素。如果說有些單元格不需要被編輯怎麼辦呢?此種情況下我們可以為那些不可編輯的單元格新增一個指定的樣式名稱,然後在把單元格變成可編輯狀態之前先檢查它是否不包含那個樣式名稱。選擇總是多樣化的,你只需找到適合你應用程式的那一種。

有哪些優點和缺點呢?

JavaScript事件代理帶來的好處有:

那些需要建立的以及駐留在記憶體中事件處理器少了。這是很重要的一點,這樣我們就提高了效能,並降低了崩潰的風險。
在DOM更新後無須重新繫結事件處理器了。如果你的頁面是動態生成的,比如說通過Ajax,你不再需要在元素被載入或者解除安裝的時候來新增或者刪除事件處理器了。
潛在的問題也許並不那麼明顯,但是一旦你注意到這些問題,你就可以輕鬆地避免它們:

你的事件管理程式碼有成為效能瓶頸的風險,所以儘量使它能夠短小精悍。

不是所有的事件都能冒泡的。blur、focus、load和unload不能像其它事件一樣冒泡。事實上blur和focus可以用事件捕獲而非事件冒泡的方法獲得(在IE之外的其它瀏覽器中)。
在管理滑鼠事件的時候有些需要注意的地方。如果你的程式碼處理mousemove事件的話你遇上效能瓶頸的風險可就大了,因為mousemove事件觸發非常頻繁。而mouseout則因為其怪異的表現而變得很難用事件代理來管理。


總結:
已經有一些使用主流類庫的事件代理示例出現了,比如說jQuery、Prototype以及Yahoo! UI。你也可以找到那些不用任何類庫的例子,比如說Usable Type blog上的這一個。
一旦需要的話,事件代理將是你工具箱裡的一件得心應手的工具,而且它很容易實現。