事件代理(事件委托)
1. 什么是事件代理
把一个元素响应事件(click、keydown 等)的函数委托到另一个元素。
事件流都经过三个阶段:捕获 → 目标 → 冒泡,事件委托就是利用冒泡阶段完成的:把一个或一组元素的事件委托到它的父层或更外层元素,真正绑定事件的是外层元素;当事件响应到目标元素时,通过事件冒泡触发外层元素的绑定函数,再在外层元素上去执行。
适合事件委托的事件:click、mousedown、mouseup、keydown、keyup、keypress。
2. 应用场景
一、减少内存消耗
如果给每个列表项都绑定一个函数,内存开销很大。改用事件委托,把点击事件绑在父层 <ul>,执行时再去匹配目标元素:
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item n</li>
</ul>
document.getElementById('list').addEventListener('click', function (e) {
var target = e.target || e.srcElement;
if (target.nodeName.toLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
二、动态绑定事件
如果用户可以随时动态增加或移除列表项,每次都要重新给新增的元素绑定、给删除的元素解绑,非常麻烦。事件委托绑在父层,和目标元素的增减无关。
const oBtn = document.getElementById('btn');
const oUl = document.getElementById('ul1');
oUl.onclick = function (ev) {
const target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
};
oBtn.onclick = function () {
const oLi = document.createElement('li');
oLi.innerHTML = 'item ' + ++num;
oUl.appendChild(oLi);
};
3. 优缺点
优点:
- 减少内存消耗(无需为每个子元素绑定)。
- 动态增删子元素时无需重新绑定。
- 简化逻辑,统一在外层处理。
缺点:
- 把所有事件都用代理可能会出现事件误判(本不该触发的事件被绑上)。
- 依赖事件冒泡,
focus/blur等不冒泡的事件需要特殊处理(用focusin/focusout或mouseenter/mouseleave模拟)。 - 层级太深或子元素过多时,事件冒泡阶段需要逐级判断目标元素,有轻微性能损耗(实际可忽略)。
来源整理自:vue3js.cn 面试官系列



