最近腾讯约了不少同学实习,小爱每天没事翻面经都翻不过来,看到一个比较经典的题目------事件代理(Event Delegation)。它是前端面试中的高频考点,特别是在腾讯等大厂的面试中经常被问到,大厂面试官通常会深入考察你对事件委托的原理、优缺点、适用场景。今天小爱触景生情,写一篇掘金文章复习一下这个经典知识,全面拆解这个知识点,顺便聊聊当年追妹子的经验(真的有吗?
事件委托
事件委托的定义
简单来说,事件委托是一种利用事件冒泡机制,在父元素上监听子元素的事件的技术。
在小爱同学还是真·小白的时候,曾经为了写含有100个li的列表,一下午把键盘噼里啪啦敲出火星(ul>li*100的事儿)
html
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<!-- ... 省略 Item 5 到 Item 99 ... -->
<li>Item 100</li>
</ul>
下午忙完后,小爱又突发奇想要为每个li
都绑上事件,结果又是一顿猛敲键盘啦(舍友:活爹)
那天晚上,聪明的小爱决定调试一下,直接在父元素上打印出事件对象。
js
ul.addEventListener('click', function(event) {
console.log(event);
});
当他点击其中一个li
时,控制台显示了一个event对象,其中就有target
属性,这个target
指向被点击的li
,那既然能拿到被点击的li
,岂不是就可以让它变大变小变漂亮了?!
js
学会了一点寄术的小爱迫不及待地给他心爱的娜娜写下浪漫的三行情诗(哎呀好甜啊呜呜
事件委托的原理
以上纯属虚构,以下才是正文内容。事件委托的核心基于事件冒泡机制。
事件流
一个事件从触发到结束分为三个阶段:捕获阶段 、目标阶段 、冒泡阶段。
过程如下:
- 捕获阶段:事件从最外层的节点,也就是文档对象(document)开始,逐级向下传播,直到事件的目标节点上。
- 目标阶段:事件到达目标节点,触发目标节点上的事件处理函数。
- 冒泡阶段:事件从目标节点开始,逐级向上传播,直到到达最外层节点------文档对象(document)
事件冒泡
冒泡这件事就和找女朋友一样,有的能找到,有的却不能😮💨
冒泡固然好,但是有人就不想要冒泡呢,这该怎么办?
按理说,点击弹窗遮罩层就能关闭弹窗,这是一个很正确的逻辑,但是为什么点击弹窗空白内容,居然也能关闭弹窗,这是什么道理?!所以能理解为什么这个世界有时候就不那么需要冒泡了吗? 这个时候只要来段
js
modal.addEventListener('click', function (event) { event.stopPropagation(); // 阻止冒泡到 overlay });
冒泡就老实了
事件委托的优缺点
优点
1. 减少事件绑定,节省资源 当你有大量子元素时,比如小爱想做一个评论区页面,每个评论都需要点击响应。如果直接给每个评论绑定事件,内存消耗会非常大。事件委托只需要给父容器绑定一次事件,就能捕获所有子元素的点击。
2. 自动支持动态添加的元素
小爱在做动态列表时,发现如果直接给每个元素绑定事件,新加的元素根本没法响应。事件委托解决了这个问题,因为事件是绑定在父元素上,后续动态添加的元素也会受到冒泡影响。
html
<h3>任务列表</h3>
<ul id="task-list">
<li>任务 A</li>
<li>任务 B</li>
</ul>
<button id="add">添加任务</button>
<script>
const taskList = document.getElementById('task-list');
const addBtn = document.getElementById('add');
// 父元素监听所有 li 的点击
taskList.addEventListener('click', function(event) {
if (event.target.tagName.toLowerCase() === 'li') {
console.log('你点击了任务:', event.target.textContent);
}
});
// 每次点击按钮,添加一个新任务
let count = 3;
addBtn.addEventListener('click', () => {
const li = document.createElement('li');
li.textContent = `任务 ${String.fromCharCode(64 + count++)}`;
taskList.appendChild(li);
});
</script>
缺点
小爱信心满满地对输入框搞了个事件委托,结果点击半天没反应。她以为是浏览器坏了,甚至重启了 VSCode(并没有用 qwq)。
html
<div id="form">
<input type="text" placeholder="请输入内容" />
</div>
<script>
const form = document.getElementById('form');
// 想监听 input 的焦点事件
form.addEventListener('focus', function(event) {
console.log('聚焦了 input');
});
</script>
聪明的你可以用本文学到的知识帮小爱看看问题出在哪里嘛?
🔍 这段代码是不会工作的!因为 focus
和 blur
事件默认不冒泡,所以事件委托抓不到它们!
同时,在事件处理程序中需要正确判断事件的目标元素,以执行相应的操作。如果判断逻辑复杂或错误,可能会导致意外行为。
html
<ul id="list">
<li><span>选项 A</span></li>
<li><span>选项 B</span></li>
</ul>
<script>
const list = document.getElementById('list');
list.addEventListener('click', function(event) {
// 以为点到了 li,结果其实点的是 span!
if (event.target.tagName.toLowerCase() === 'li') {
console.log('你点击了选项:', event.target.textContent);
}
});
</script>
🚨 这段代码点 span
是不会执行的,因为 实际上的event.target
是 span
而不是 li
!
对了,文章最后放上小爱同学和娜娜一起看樱花的照片,春天到了,掘友们有踏春吗😏