前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库事件委托 动态添加元素 实现
浏览器事件系统

动态新增的列表项,为什么一个父级点击监听就够?

把 click 监听器放在稳定的列表父元素上,点击子项时事件冒泡到父级。新插入的按钮不需要重新绑定监听器;回调里再从 event.target 找到对应项。

前端进阶之旅 · 一题精讲更新于 2026.09.29
浏览器#事件系统
先看核心答案读代码示例
理解线索

监听器绑在稳定祖先上

  1. 事件冒泡子项事件沿祖先链传播
  2. event.target此次事件起始目标
  3. closest()从目标向上寻找匹配元素

并非所有事件都会冒泡,委托策略要按事件类型选择。

核心回答

先记住这个答案

动态列表若给每个删除按钮单独绑定监听器,新插入按钮还要再绑,移除节点时也容易遗漏清理。事件委托把一条 click 监听放在稳定父容器上,按钮点击会冒泡到这里;处理器用 event.target.closest() 找到对应按钮和列表项。只要新项仍是该容器后代,它也能沿用这条监听器。

  • 父级监听覆盖未来子项
  • 依赖事件沿路径向上传播
  • closest() 找匹配的业务节点
  • 确认节点仍属于当前容器

待办列表删除当前项

列表项从接口实时追加。父级始终存在,因此只需在父级注册一次事件。点击图标时,实际目标可能是按钮里的 <svg>,直接判断 event.target.matches("button") 会漏掉;向上找按钮更稳。

父级委托删除按钮JavaScript
const list = document.querySelector('#todos')
list.addEventListener('click', event => {
  const button = event.target.closest('[data-remove]')
  if (!button || !list.contains(button)) return
  const item = button.closest('[data-todo-id]')
  if (item) item.remove() // 动态插入的待办项同样生效
})

contains() 确认找到的按钮属于当前列表,避免在复杂嵌套中错把外部节点当作目标。新插入的列表项只要带同样属性,便可复用这段处理器。 若点击会触发别的组件的停止传播,父级可能收不到;若列表处于 Shadow DOM 内,还要考虑事件是否跨越边界和目标重定向。

委托不是无条件省内存

委托确实减少大量重复的监听器,也简化动态节点清理。但父级每收到一次 click 都要判断目标,复杂页面若把所有事件挂到 document,逻辑会难以维护。

选择足够稳定且范围尽量小的共同祖先。委托适合大量同类子项的统一动作;只有少数固定控件时,直接监听往往更清楚。

示例使用 closest('[data-remove]'),是为了允许用户点到按钮内的图标;再找 closest('[data-todo-id]'),是为了把按钮映射回所属待办。若按钮被放到列表项外的浮动操作栏,这层关系就不存在,委托逻辑也得改。它省的是重复注册,不是省掉对 DOM 结构的约定。

从服务器追加节点时,父容器的监听器不必重新绑定;但如果整个列表容器被框架重新渲染替换,旧监听器会留在旧节点上。要么在稳定的更外层注册,要么随组件生命周期重新注册并清理。排查“新按钮没反应”时,先确认换的是列表项还是父容器。

回答前,多想一步

容易答错的地方

直接把 event.target 当成按钮
用户可能点到按钮内部图标或文字,应向上找到业务节点。
以为委托能处理任何事件
不冒泡事件、跨 Shadow DOM 的事件和被阻断传播的路径都需要另看。
试着用自己的话回答

面试官还会怎么问?

target 与 currentTarget 在例子中各是什么?

target 是实际点击的内部节点,currentTarget 是绑定监听器的列表父元素。

为什么还要检查 contains()?

防止 closest() 找到不属于该业务容器的匹配节点。

父元素也动态替换怎么办?

监听器会跟旧父节点一起离开;应在稳定祖先上委托,或重新绑定新父元素。

从一道题,走向一组知识

把知识连起来

事件系统

事件回调中 event.target 与 event.currentTarget 有何区别?在事件委托中应使用哪个?

target 是起始目标,currentTarget 是父级监听器所在元素。

事件系统

哪些事件不能直接靠冒泡委托?

不冒泡事件需要捕获或替代事件,不能机械套用点击委托。

参考资料

  • MDN 参考文档

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 待办列表删除当前项
  3. 委托不是无条件省内存
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑