事件的捕獲、冒泡和委託

事件捕獲、事件冒泡、事件委託,這三個類似又不盡相同的術語把我搞懵了很長一段時間,今天專門抽時間挨個看了一遍。設計模式

首先,是那個聞名遐邇的圖瀏覽器

clipboard.png

事件捕獲和事件冒泡是事件流機制層面的東西,不以代碼的意志爲轉移。函數

DOM2級規範(瀏覽器自身的事件規範)要求事件應該從document對象開始向下傳播,找到具體的目標前,整個過程都是捕獲階段。性能

找到具體的目標後,開始向外層冒泡,直到回到document對象結束,這個過程叫冒泡階段。spa

但規範只是規範,較舊的瀏覽器都是從window對象開始捕獲事件的,建議放心地使用事件冒泡,特殊狀況下再考慮事件捕獲。設計

接下來是事件委託,這個纔是重點!code

JS和HTML之間是靠事件來實現交互的,若是有多個div須要綁定click事件監聽,不管是DOM0級和DOM2級,添加到頁面上的事件處理程序數量將直接關係到頁面的總體性能。對象

  • 首先,每一個函數都是對象,都會佔內存。內存中的對象越多,性能就越差。
  • 其次,必須事先指定全部事件處理程序而致使的DOM訪問次數,會延遲整個頁面的交互就緒時間。也就是說在多個節點上掛載事件,會影響DOM的加載。

事件委託就是解決性能問題的,你能夠把它看作是一種解決方案或設計模式。blog

事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。事件

少廢話,上代碼(如下例子來自《Javascript高級程序設計》,略刪改)

// 面對這樣一段HTML
<ul id="myLinks">
    <li id="goSomewhere"></li>
    <li id="doSomething"></li>
    <li id="sayHi"></li>
</ul>

不使用事件委託的笨辦法:

var item1 = document.getElementById('goSomewhere')
var item2 = document.getElementById('doSomething')
var item3 = document.getElementById('sayHi')

item1.addEventListener('click', function(e) {
}, false)
item2.addEventListener('click', function(e) {
}, false)
item3.addEventListener('click', function(e) {
}, false)

使用事件委託,對於用戶來講最終的結果相同,但咱們其實只取了一個DOM元素,只添加了一個事件處理程序,但這種實現方案所須要佔用的內存更少。

var list = document.getElementById('myLinks')

list.addEventListener('click', function(e) {
    var target = e.target;
    switch(target.id) {
        case 'goSomewhere':
            // TODO
            break;
        case 'doSomething':
            // TODO
            break;
        case 'sayHi':
            // TODO
            break;
    }
}, false)

若是可行的話,能夠考慮爲document對象添加一個事件處理程序,優勢以下:

  • document對象在DOM樹渲染的初期就已經準備完畢,無需等待DOMContentLoaded或load事件,並且document對象不像其餘DOM節點那樣會被移除。換句話說,只要目標元素呈如今頁面上,就已經具有了事件處理程序。
  • 因爲只引用一個DOM對象用來添加事件處理程序,在頁面中設置事件處理程序所需的時間更少。
  • 整個頁面佔用的內存更少,提高總體性能。

關於上面三點的最後一點,解釋一下:若是咱們沒有使用事件委託,那在具體元素好比某div上添加的監聽,在該div被移除出DOM樹後,綁在它身上的事件處理程序可能沒法被垃圾回收,形成浪費。

每當將事件處理程序指定給元素時,運行中的瀏覽器代碼與支持頁面交互的JS代碼之間會創建一個鏈接,這種鏈接越多,頁面執行效率越低,利用事件委託能夠儘量減小這種鏈接數量。呃……雖然可能也無法成功垃圾回收,但總體性能仍是更高的。

相關文章
相關標籤/搜索