从经典面试题事件委托到撩妹

最近腾讯约了不少同学实习,小爱每天没事翻面经都翻不过来,看到一个比较经典的题目------事件代理(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 复制代码

学会了一点寄术的小爱迫不及待地给他心爱的娜娜写下浪漫的三行情诗(哎呀好甜啊呜呜

事件委托的原理

以上纯属虚构,以下才是正文内容。事件委托的核心基于事件冒泡机制。

事件流

一个事件从触发到结束分为三个阶段:捕获阶段目标阶段冒泡阶段

过程如下:

  1. 捕获阶段:事件从最外层的节点,也就是文档对象(document)开始,逐级向下传播,直到事件的目标节点上。
  2. 目标阶段:事件到达目标节点,触发目标节点上的事件处理函数。
  3. 冒泡阶段:事件从目标节点开始,逐级向上传播,直到到达最外层节点------文档对象(document)
sequenceDiagram participant D as Document participant B as Body participant P as Parent Element participant T as Target Element Note right of D: 事件触发过程 D->>B: 捕获阶段(从 Document 向下传递) B->>P: 捕获阶段(继续向下) P->>T: 捕获阶段(到达目标元素) T->>T: 目标阶段(事件在目标上触发) T->>P: 冒泡阶段(事件从目标向上冒泡) P->>B: 冒泡阶段(继续向上传递) B->>D: 冒泡阶段(到达 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>

聪明的你可以用本文学到的知识帮小爱看看问题出在哪里嘛?

🔍 这段代码是不会工作的!因为 focusblur 事件默认不冒泡,所以事件委托抓不到它们!

同时,在事件处理程序中需要正确判断事件的目标元素,以执行相应的操作。如果判断逻辑复杂或错误,可能会导致意外行为。

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.targetspan 而不是 li

对了,文章最后放上小爱同学和娜娜一起看樱花的照片,春天到了,掘友们有踏春吗😏

相关推荐
uhakadotcom32 分钟前
阿里云Tea OpenAPI:简化Java与阿里云服务交互
后端·面试·github
木木黄木木38 分钟前
css炫酷的3D水波纹文字效果实现详解
前端·css·3d
美食制作家41 分钟前
【无标题】Threejs第一个3D场景
javascript·three
郁大锤1 小时前
Flask与 FastAPI 对比:哪个更适合你的 Web 开发?
前端·flask·fastapi
uhakadotcom1 小时前
图像识别中的三大神经网络:Inception、ResNet和VGG
算法·面试·github
uhakadotcom2 小时前
DeepFM算法:提升CTR预估和推荐系统的强大工具
算法·面试·github
HelloRevit2 小时前
React DndKit 实现类似slack 类别、频道拖动调整位置功能
前端·javascript·react.js
ohMyGod_1232 小时前
用React实现一个秒杀倒计时组件
前端·javascript·react.js
uhakadotcom3 小时前
Python 中的 @staticmethod 和 @classmethod 详解
后端·面试·github
eternal__day3 小时前
第三期:深入理解 Spring Web MVC [特殊字符](数据传参+ 特殊字符处理 + 编码问题解析)
java·前端·spring·java-ee·mvc