javascript 事件代理,原理和問題

有以下 html 片斷html

<ul>
    <li>11111111111</li>
    <li>22222222222</li>
    <li>33333333333</li>
</ul>

要對 li 添加 click 事件。一般作法:瀏覽器

var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
    list[i].addEventListener('click', function(e) {
        console.log(e.target.textContent);
    })
}

事件代理的作法dom

var ul = document.getElementsByTagName('ul')[0];
ul.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
        console.log(e.target.textContent);
    }
})

咱們來對比一下兩段註冊點擊事件的代碼。第一段代碼很常規,直接找到相關元素而後一一綁定。第二段代碼看起來簡潔一點,但多了一層判斷。wordpress

作一個簡單的 delegate 封裝函數

EventTarget.prototype.delegate = function(targetDom, type, listener) {
    this.addEventListener(type, function(e) {
        if (e.target.tagName === targetDom.toUpperCase()) {
            listener(e);
        }
    });
}

var ul = document.getElementsByTagName('ul')[0];
ul.delegate('li', 'click', function(e) {
    console.log(e.target.textContent);
});

接下來的篇幅主要會聊到事件流,事件代理的原理和問題,event 和 EventTarget 幫你深度瞭解 js 的事件。ui

事件觸發的流程

一般咱們把事件流分爲三個階段this

var ul = document.getElementsByTagName('ul')[0];
ul.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
        console.log(e.target.textContent);
    }
})
document.body.addEventListener('click', function(e)     {
    console.log('body', e.target.textContent);
})
  1. 捕獲階段:這個就是向下去找觸發事件的元素。可是必定注意若是這個時候 body 上的 click 並不會觸發。若是但願 body 上的 click 先觸發,就須要給 addEventListener 第三個參數 useCapture 設置爲 true (dom3 引入的,因此比較新的瀏覽器纔有效)。spa

  2. 目標階段:執行目標的綁定函數prototype

  3. 冒泡階段:沿着父級元素一路向上,body 上若是也有 click 事件就會執行。event.stopPropagation() 能夠用於事件處理函數中阻止冒泡行爲。代理

事件代理的原理和問題

在咱們瞭解了事件流以後,事件代理的原理就很好理解了,其實就是事件冒泡會觸發容器 dom 的相關事件並執行監聽函數。

那事件代理會帶來什麼問題呢?

<ul>
    <li>111111</li>
</ul>

若是 ul 有 click ,而且在它上面爲 li 作了 click 事件的代理。這個時候其實我只想觸發 li 的 click。解決方法以下:

var ul = document.getElementsByTagName('ul')[0];
ul.addEventListener('click', function(e) {
    if (e.target === e.currentTarget) {
        console.log(e.target);
    }
});
ul.delegate('li', 'click', function(e) {
    console.log(e.target);
});

很顯然 stopPropagation 並不能解決問題,這裏經過比較 target 和 currentTarget 來判斷當前事件觸發元素(target)是不是註冊事件的元素(currentTarget)便可。

stopPropagation,target,currentTarget 都是 event 的方法和屬性,因此下面詳細講講 event 對象。

event 事件對象詳解

經常使用屬性:

  • currentTarget 註冊事件的 dom 元素

  • srcElement ie6-8 的觸發事件的 dom 元素,非標準

  • target 觸發事件的 dom 元素

  • timeStamp 返回事件發生時的時間戳

  • type 事件的類型

經常使用方法

  • preventDefault() 阻止默認行爲。例如 a 標籤默認會跳轉 href 指定的連接,執行該方法能夠阻止跳轉的發生。

  • stopImmediatePropagation() 阻止冒泡行爲,並當即阻止該綁定元素上的相同類型事件處理函數的執行。當即阻止的意義在於,本方法執行以前函數執行或者其餘的相同類型綁定不會有問題,但一旦執行該方法後面的程序將再也不執行。例子

  • stopPropagation() 阻止冒泡行爲。

EventTarget

咱們來看一下 mdn 對於 EventTarget 的定義:
一個EventTarget是一個能夠接受DOM事件且能綁定事件監聽器的對象.最多見的EventTarget就是DOM元素對象,另外,還有一些不是DOM元素的對象也能成爲EventTarget,好比document, window, XMLHttpRequest,等等.

EventTarget 有三個經常使用方法:

  1. addEventListener(type, listener[, useCapture])

    給 EventTarget 添加事件綁定,type 爲事件類型(例如 ‘click’),listener 事件觸發時的處理函數(默認參數 event 本文後面會講到)。useCapture 是一個非必需的布爾值,默認爲 false。若是設置爲 true 即表示但願發起捕獲(關於捕獲,會在後面的事件觸發流程裏講到)。
  2. removeEventListener(type, listener[, useCapture])

給 EventTarget 註銷事件綁定。參數和 addEventListener 相同。須要特別注意兩點。

一、若是同一個監聽事件分別爲「事件捕獲」和「事件冒泡」註冊了一次,一共兩次,這兩次事件須要分別移除。二者不會互相干擾。
二、若是此事件正在執行,會當即中止。
  1. dispatchEvent

手動觸發事件,舉個例子(來自 mdn):

var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) {
    console.log(e.target)
}, false);

// Dispatch the event.
elem.dispatchEvent(event);

上面這個例子就是一個簡單的自定義事件,若是想深刻了解能夠看張鑫旭的漫談js自定義事件、DOM/僞DOM自定義事件

相關文章
相關標籤/搜索