天天看點

點選頁面其它地方隐藏div所想到的jQuery的delegate

在網頁開發的過程中經常遇到的一個需求就是點選一div内部做某些操作,而點選頁面其它地方隐藏該div。比如很多導航菜單,當菜單展開的時候,就會要求點選頁面其它非菜單地方,隐藏該菜單。

先從最簡單的開始,假如頁面有一個id為test的div,我們要實作點選頁面其它地方隐藏該div:

IE的事件冒泡:事件開始時由最具體的元素接收,然後逐級向上傳播到較為不具體的元素

Netscape的事件捕獲:不太具體的節點更早接收事件,而最具體的元素最後接收事件,和事件冒泡相反

DOM事件流:DOM2級事件規定事件流包括三個階段,事件捕獲階段,處于目标階段,事件冒泡階段,首先發生的是事件捕獲,為截取事件提供機會,然後是實際目标接收事件,最後是冒泡句階段。

Opera、Firefox、Chrome、Safari都支援DOM事件流,IE不支援事件流,隻支援事件冒泡

如有以下html,點選div區域,按照不同的模型事件元素的click事件觸發順序如下所示:

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

在觸發DOM上的某個事件的時候會産生一個事件對象event,這個對象包含着所有與事件有關的資訊,包括産生事件的元素、事件類型等相關資訊。所有浏覽都支援event對象,但支援方式不同。事件對象有一個方法(W3C:stopPropagation)/屬性(IE:cancelBulle=true)可以阻止事件繼續冒泡或捕獲。我們如果想在事件冒泡到某元素時阻止冒泡可以寫一個這樣的相容浏覽器方法:

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

因為所有的浏覽器都支援事件冒泡,浏覽器相容性考慮,我們一般綁定事件的的時候都會利用事件冒泡而不是事件捕獲。了解了這個之後我們可以看看下面兩種思路了。

第一種思路分兩步

第一步:對document的click事件綁定事件處理程式,使其隐藏該div

第二步:對div的click事件綁定事件處理程式,阻止事件冒泡,防止其冒泡到document,而調用document的onclick方法隐藏了該div。

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

這樣當點選頁面非div區域的時候,直接或層層冒泡會調用document的onclick方法,隐藏該div,而點選div或其子元素的時候,事件總會冒泡的div本身,這時候會阻止事件繼續冒泡,不會調用doument的onclick方法緻使div被隐藏,進而完成了我們的需求。

我們之前提到,在觸發DOM上的某個事件的時候會産生一個事件對象event,這個對象包含着所有與事件有關的資訊,包括産生事件的元素、事件類型等相關資訊,思路一中div的click事件處理程式傳入的參數就是這個event對象。通路IE中的event對象有幾種不同的方式,取決于指定事件處理程式的方法。直接為DOM元素添加事件處理程式時,event對象作為window對象的一個屬性存在。

event對象包含了一個重要屬性:target(W3C)/srcElement(IE),這個屬性辨別了觸發事件的原始元素,思路二就是要利用這個屬性。我們可以直接對document的click事件綁定事件處理程式,在事件處理程式中判讀事件源是否為id==test的div元素或其子元素,如果是則方法return不做操作,如果不是則隐藏該div。

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

這樣當點選頁面任何地方的時候都會層層冒泡至document的click事件,事件處理程式會判斷事件源是否為id==test的div或其子元素,如果是方法return,否則隐藏該div,也能夠實作我們的需求。

這兩種思路都依賴于事件冒泡,是以我們在處理其它相關元素的click事件的時候一定要注意這點,避免其他相關元素的click事件處理程式中包含阻止事件冒泡代碼而影響了該功能。

這兩種方式都很容易了解,貌似思路一更優秀一些,看起來它的處理更簡單一些,不用去層層判斷事件源,直接把click事件綁定在該div上。在這個例子中确實如此,但是有些複雜的頁面就不盡然了,假如我們有一個頁面,上面有數十個div都需要點選頁面其它地方隐藏這類問題

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

我們用思路一寫出的代碼可能是這樣:

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

看起來簡單依舊的樣子,但是我們仔細想想就會發現問題,我們在每個dialog上都綁定了類似的方法,維護如此多的click事件處理程式對記憶體來說絕對是可開銷,導緻我們頁面運作緩慢。而且如果我們可以動态使用ajax建立新dialog問題又來了,新建立的dialog不能實作隐藏功能!因為綁定函數已經執行完了,不會再為新的dialog綁定click事件處理程式,我們隻能自己來做此事。也就是說思路一無法把處理程式附加到可能還未存在于DOM中的DOM元素之上。因為它是直接把處理程式綁定到各個元素上,它不能把處理程式綁定到還未存在于頁面中的元素之上。

這時候就是思路二展示身手的時候了,我們看看思路二在這種時候代碼的書寫

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

改動也相當的小,我們來看看是不是能解決上邊的兩個問題了,首先無論多少個dialog我們隻是綁定了一個click事件處理程式,對性能影響不大,添加一個新的dialog思路二的代碼還好不好使呢,依舊好使,這樣我們就能發現在複雜頁面的情況下實際上思路二是一種更優秀的解決方案。

這些都明白了,我們就能說說本文的第二個主角jQuery的delegate方法了。

首先看看jQuery官方對delegate的文法及描述

.delegate( selector, eventType, handler(eventObject) )

Description: Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.

delegate() 方法為指定的元素(屬于被選元素的子元素)添加一個或多個事件處理程式,并規定當這些事件發生時運作的函數。

使用 delegate() 方法的事件處理程式适用于目前或未來的元素(比如由腳本建立的新元素)。

通過上面語句我們就可以為所有table的td綁定click事件處理程式。

delegate方法設計意圖在于把處理程式附加到單個元素上或是一小組元素之上,監聽後代元素上的事件而不是循環周遊并把同一個函數逐個附加到DOM中的多個個元素上。把處理程式附加到一個(或是一小組)祖先元素上而不是直接把處理程式附加到頁面中的所有元素上,進而帶來性能上的優化。

通過上面知識我們可以發現jQuery的delegate方法可以友善實作我們隐藏div的需求

點選頁面其它地方隐藏div所想到的jQuery的delegate
點選頁面其它地方隐藏div所想到的jQuery的delegate

使用jQuery我們發現比我們思路二在性能上又有了小幅提升,因為我們不需要冒泡至document處理了,隻需要在dialog的父元素就可以處理完成了,可以不至于把很多類似功能都綁定到document上,需要注意的一點就是jQuery已經貼心的幫我們把this處理為事件源,處理起來更是如魚得水了。

通過上面我們說一堆我們可以在權衡使用bind還是delegate上有一定依據了,如果就單獨綁定一個元素的事件處理程式,用bind還是很合适的,但是如果處理很多類似元素的事件處理程式的時候不妨考慮一下delegate,看看是否對提高性能有所幫助。

   本文轉自魏瓊東部落格園部落格,原文連結:http://www.cnblogs.com/dolphinX/p/3239530.html,如需轉載請自行聯系原作者

繼續閱讀