解釋下什麼是事件代理?應用場景?

林恒發表於2024-05-18

一、是什麼

事件代理,俗地來講,就是把一個元素響應事件(clickkeydown......)的函式委託到另一個元素

前面講到,事件流的都會經過三個階段: 捕獲階段 -> 目標階段 -> 冒泡階段,而事件委託就是在冒泡階段完成

事件委託,會把一個或者一組元素的事件委託到它的父層或者更外層元素上,真正繫結事件的是外層元素,而不是目標元素

當事件響應到目標元素上時,會透過事件冒泡機制從而觸發它的外層元素的繫結事件上,然後在外層元素上去執行函式

下面舉個例子:

比如一個宿舍的同學同時快遞到了,一種笨方法就是他們一個個去領取

較優方法就是把這件事情委託給宿舍長,讓一個人出去拿好所有快遞,然後再根據收件人一一分發給每個同學

在這裡,取快遞就是一個事件,每個同學指的是需要響應事件的 DOM元素,而出去統一領取快遞的宿舍長就是代理的元素

所以真正繫結事件的是這個元素,按照收件人分發快遞的過程就是在事件執行中,需要判斷當前響應的事件應該匹配到被代理元素中的哪一個或者哪幾個

二、應用場景

如果我們有一個列表,列表之中有大量的列表項,我們需要在點選列表項的時候響應一個事件

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

如果給每個列表項一一都繫結一個函式,那對於記憶體消耗是非常大的

// 獲取目標元素
const lis = document.getElementsByTagName("li")
// 迴圈遍歷繫結事件
for (let i = 0; i < lis.length; i++) {
    lis[i].onclick = function(e){
        console.log(e.target.innerHTML)
    }
}

這時候就可以事件委託,把點選事件繫結在父級元素ul上面,然後執行事件的時候再去匹配目標元素

// 給父層元素繫結事件
document.getElementById('list').addEventListener('click', function (e) {
    // 相容性處理
    var event = e || window.event;
    var target = event.target || event.srcElement;
    // 判斷是否匹配目標元素
    if (target.nodeName.toLocaleLowerCase === 'li') {
        console.log('the content is: ', target.innerHTML);
    }
});

還有一種場景是上述列表項並不多,我們給每個列表項都繫結了事件

但是如果使用者能夠隨時動態的增加或者去除列表項元素,那麼在每一次改變的時候都需要重新給新增的元素繫結事件,給即將刪去的元素解綁事件

如果用了事件委託就沒有這種麻煩了,因為事件是繫結在父層的,和目標元素的增減是沒有關係的,執行到目標元素是在真正響應執行事件函式的過程中去匹配的

舉個例子:

下面html結構中,點選input可以動態新增元素

<input type="button" name="" id="btn" value="新增" />
<ul id="ul1">
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
    <li>item 4</li>
</ul>

使用事件委託

const oBtn = document.getElementById("btn");
const oUl = document.getElementById("ul1");
const num = 4;

//事件委託,新增的子元素也有事件
oUl.onclick = function (ev) {
    ev = ev || window.event;
    const target = ev.target || ev.srcElement;
    if (target.nodeName.toLowerCase() == 'li') {
        console.log('the content is: ', target.innerHTML);
    }

};

//新增新節點
oBtn.onclick = function () {
    num++;
    const oLi = document.createElement('li');
    oLi.innerHTML = `item ${num}`;
    oUl.appendChild(oLi);
};

可以看到,使用事件委託,在動態繫結事件的情況下是可以減少很多重複工作的

三、總結

適合事件委託的事件有:clickmousedownmouseupkeydownkeyupkeypress

從上面應用場景中,我們就可以看到使用事件委託存在兩大優點:

  • 減少整個頁面所需的記憶體,提升整體效能
  • 動態繫結,減少重複工作

但是使用事件委託也是存在侷限性:

  • focusblur這些事件沒有事件冒泡機制,所以無法進行委託繫結事件

  • mousemovemouseout這樣的事件,雖然有事件冒泡,但是隻能不斷透過位置去計算定位,對效能消耗高,因此也是不適合於事件委託的

如果把所有事件都用事件代理,可能會出現事件誤判,即本不該被觸發的事件被繫結上了事件

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

解釋下什麼是事件代理?應用場景?

相關文章