这是和冒泡/捕获并列的必考题,几乎每场前端面试都会问。
第 8 题:什么是事件委托?它的原理是什么?有哪些优点和常见坑?
🎯 面试官关注点
- 事件委托的概念
- 为什么能实现委托(依赖事件冒泡机制)
- 事件委托的优点
- 常见使用场景
- 常见坑(很爱考)
一、什么是事件委托?(一句话回答)
事件委托就是把子元素的事件,交给它的父级统一处理。
例如,你不直接给每个 li 绑定点击事件,而是给 ul 绑定一个事件,由它根据事件目标(event.target)来判断是哪个 li 被点击。
二、事件委托的工作原理是什么?
依赖事件冒泡机制。
事件从子节点冒泡到父节点,因此可以在父节点捕获到子节点触发的事件。
示例:
HTML:
html
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
JavaScript:
javascript
list.addEventListener("click", e => {
if (e.target.tagName === "LI") {
console.log("点击了:", e.target.innerText)
}
})
- 无需为每个
li单独绑定 - 父元素就能知道哪个
li被点击
三、事件委托的优点(面试官最喜欢听的点)
1. 大量元素只需要绑定 1 个事件(提升性能)
如果你有几百个子节点:
javascript
// ❌ 不推荐:为每个元素单独绑定
li.addEventListener('click', ...)
变成:
javascript
// ✅ 推荐:只在父元素绑定一次
ul.addEventListener('click', ...)
浏览器少绑定数百倍的事件处理器。
2. 可以处理动态新增的元素
后添加的 li 元素也能自动拥有点击功能:
javascript
ul.innerHTML += '<li>新项</li>'
无需再手动绑定事件。
3. 节省内存,减少事件绑定次数
- 更少的监听器
- 更少的内存占用
- 更少的浏览器回流/重绘触发
四、事件委托的常见使用场景
- ✓ 列表点击(最常见)
- ✓ 菜单、Tab、Table、下拉框
- ✓ 动态插入的 DOM 元素
- ✓ 兼容旧浏览器的"模拟冒泡"(如 input focus/blur)
五、事件委托的常见坑(高频考点)
❌ 1. event.target 不一定是你想要的元素
例如 li 内嵌了 span:
html
<li><span>文字</span></li>
你点的是 span,但 event.target 是 span,不是 li。
解决方案:
方法 1:往上找最近的 li
javascript
const li = e.target.closest("li")
方法 2:判断祖先链
javascript
let node = e.target
while (node && node !== ul) {
if (node.tagName === 'LI') {
// 找到了
break;
}
node = node.parentNode
}
❌ 2. 不支持事件捕获阶段的冒泡
比如:
blur/focus不冒泡mouseenter/mouseleave不冒泡
要用事件委托时,要用它们的替代事件:
- ✓
focus→focusin - ✓
blur→focusout - ✓
mouseenter→mouseover - ✓
mouseleave→mouseout
❌ 3. 停止冒泡会影响委托
如果子元素用:
javascript
event.stopPropagation()
父级监听不到事件。
速记卡(10 秒复盘)
🔶 事件委托 = 父元素监听,判断子元素事件目标
基于事件冒泡。
🔶 优点:性能提升、可处理动态 DOM、节省事件绑定
大量节点 → 只绑一次。
🔶 坑:event.target 不可靠 → 用 e.target.closest()
尤其 li 嵌套 span 时。
🔶 不冒泡事件不能委托
focus/blur、mouseenter/mouseleave。