一、是什么
事件代理,俗地來講,就是把一個元素響應事件(click
、keydown
......)的函數委托到另一個元素
我們知道,事件流的都會經過三個階段:捕獲階段 -> 目標階段 -> 冒泡階段,而事件委托就是在冒泡階段完成
事件委托,會把一個或者一組元素的事件委托到它的父層或者更外層元素上,真正綁定事件的是外層元素,而不是目標元素
當事件響應到目標元素上時,會通過事件冒泡機制從而觸發它的外層元素的綁定事件上,然后在外層元素上去執行函數
下面舉個例子:
比如一個宿舍的同學同時快遞到了,一種笨方法就是他們一個個去領取
較優方法就是把這件事情委托給宿舍長,讓一個人出去拿好所有快遞,然后再根據收件人一一分發給每個同學
在這里,取快遞就是一個事件,每個同學指的是需要響應事件的 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"); let 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); };
可以看到,使用事件委托,在動態綁定事件的情況下是可以減少很多重復工作的
三、總結
適合事件委托的事件有:click
,mousedown
,mouseup
,keydown
,keyup
,keypress
從上面應用場景中,我們就可以看到使用事件委托存在兩大優點:
- 減少整個頁面所需的內存,提升整體性能
- 動態綁定,減少重復工作
但是使用事件委托也是存在局限性:
-
focus
、blur
這些事件沒有事件冒泡機制,所以無法進行委托綁定事件 -
mousemove
、mouseout
這樣的事件,雖然有事件冒泡,但是只能不斷通過位置去計算定位,對性能消耗高,因此也是不適合於事件委托的