關於JS中事件代理的解析

概述

通常來講,咱們在爲前端頁面設計交互的的時候每每須要爲DOM元素添加事件處理程序。可是不少時候頁面的DOM元素的結構和層級會很複雜,若是咱們爲全部須要添加事件處理的DOM元素一一綁定上事件處理程序,那麼不只編寫出的代碼會很繁雜,整個頁面的性能也會很低下。好比咱們有一個有序或者無序的列表,其中包裹了數百個子節點li,通常來講,在經過選擇器拿到元素集合後,咱們會用for循環對集合進行遍歷,而後爲其添加事件處理方法,這麼作會帶來什麼結果呢,在JS引擎中,代碼操做的DOM數量和添加的事件處理程序數量直接關係到頁面的總體性能。操做的DOM數量越多,對DOM的訪問次數越多,瀏覽器的重繪次數就愈來愈多,頁面的響應速度和運行性能也就愈來愈差。因此,在進行程序優化的過程,其中一個重要的思路就是減小DOM的操做。前端

這時,事件代理存在的重要意義就體現出來了,它爲咱們提供了一種新的解決爲大量相似的DOM元素添加事件處理的解決方案。只爲一個容器或者說父節點添加一次事件處理程序,就達成了控制這個容器中一系列元素的目標,大大的減小了瀏覽器對DOM的訪問。node

事件代理原理

事件代理本質上來講是利用事件冒泡的機制來進行實現的。何爲事件冒泡呢,百科中的解釋是當事件發生後,這個事件就要開始傳播(從裏到外或者從外向裏)。爲何要傳播呢?由於事件源自己(可能)並無處理事件的能力,即處理事件的函數(方法)並未綁定在該事件源上。例如咱們點擊一個按鈕時,就會產生一個click事件,但這個按鈕自己可能不能處理這個事件,事件必須從這個按鈕傳播出去,從而到達可以處理這個事件的代碼中(例如咱們給按鈕的onclick屬性賦一個函數的名字,就是讓這個函數去處理該按鈕的click事件),或者按鈕的父級綁定有事件函數,當該點擊事件發生在按鈕上,按鈕自己並沒有處理事件函數,則傳播到父級去處理。瀏覽器

相關實現

具體怎麼實現事件代理呢,咱們來看一些簡單的相關例子:函數

先寫一個簡單的有序列表結構。性能

<ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ol>

好比咱們想要實現的是點擊li,彈出li的序列。若是要直接操做li。優化

var lists = document.querySelectorAll('li');
for(let i = 0; i < lists.length; i ++){
    lists[i].onclick =function () {
        alert(lists[i].innerText);
    }
}

經過選擇器拿到了li的集合,而後進行遍歷並一一綁定點擊事件,若是使用事件代理來實現呢編碼

var lists = document.querySelector('ol');
lists.onclick = function (event) {
    alert(event.target.innerText);
}

經過選擇器拿到li的容器也就是父節點ol,而後只綁定一次點擊事件,並經過event對象提供的屬性target指向容器的子節點,而後彈出該子節點的文本內容。須要瞭解的是,event對象提供了兩個屬性,分別是currentTarget和target,前者指向的是綁定事件的當前節點,後者則指向當前節點的一級子節點。當咱們面臨事件冒泡和事件捕獲時,這兩個屬性可以爲咱們提供更高的靈活度。設計

上訴例子中,咱們須要添加事件的DOM元素都是相同的,事件的種類也是同樣的,若是咱們面對的是結構更復雜一些的DOM集合呢,來看下面的例子。代理

DOM結構:code

<div class="wrap">
    <button>清空</button>
    <input type="submit" value="提交">
    <input type="text">
    <input type="checkbox">
</div>

在這樣的DOM結構中,咱們要爲四種不一樣的元素添加事件控制,若是隻是經過target指向的話是沒法實現的,須要挖掘target更深層的屬性來進行條件判斷,而target具有的屬性就是指向的子節點所具有的屬性,好比nodeName、id、className、value等

let box = document.querySelector('.wrap');
box.onclick = function (event) {
    if(event.target.nodeName === 'button'){
        //...
    }else if(event.target.value === '提交'){
        //...
    }else if(event.target.className === 'text'){
        //...
    }else{
        //...
    }
};

這樣咱們一樣只在父節點上綁定一次事件就完成了咱們想要的效果,不管是編碼效率和性能損耗都比不適用事件代理的狀況下更加優化。咱們能夠發現,相比與遍歷元素集合的方式,事件代理最大的好處就是減小了DOM的操做,歷來提高了效能。

適用場景

事件代理並不是在全部場景中都適用的,在使用事件代理時,咱們須要考慮添加事件處理的父節點可否觸發咱們想要綁定的事件,好比:

<div class="wrap">
    <input type="text"><br>
    <input type="text">
</div>

當咱們想爲容器中的文本框綁定blur事件改變框體顏色時

let box = document.querySelector('.wrap');
box.addEventListener('blur',function (event) {
    event.target.style.borderColor = 'red';
});

咱們發現這樣作是沒法生效的,應爲div元素是沒法觸發onblur事件的,同時還有其餘有關的輸入框事件也是沒法觸發的,如oninput、onfocus等。

相關文章
相關標籤/搜索