当前位置: 首页 > news >正文

DOM 交互补充:事件委托、可见性与 rAF

系列文章目录

《JavaScript 基础与进阶笔记》(前期偏基础巩固与常见面试点,后续进入闭包、异步、工程化等进阶主题)

  • 第 01 篇:数据类型与类型判断
  • 第 02 篇:变量声明与作用域
  • 第 03 篇:闭包与高阶函数
  • 第 04 篇:函数工厂
  • 第 05 篇:this 指向与绑定
  • 第 06 篇:原型与原型链
  • 第 07 篇:类与继承
  • 第 08 篇:JS 执行机制与异步队列
  • 第 09 篇:数组常用方法
  • 第 10 篇:字符串算法
  • 第 11 篇:常见手写题合集(上)
  • 第 12 篇:常见手写题合集(下)
  • 第 13 篇:Promise 与 async/await
  • 第 14 篇:数据结构基础
  • 第 15 篇:垃圾回收与内存
  • 第 16 篇:DOM 基础全面解析
  • 第 17 篇:DOM 性能与渲染
  • 第 18 篇:DOM 交互补充(本文)

文章目录

  • 系列文章目录
  • 前言
  • 一、事件流:捕获与冒泡
  • 二、`target` 与 `currentTarget`
  • 三、事件委托(面试重点)
    • 3.1 是什么
    • 3.2 为什么用
    • 3.3 局限
  • 四、不冒泡时的替代
  • 五、IntersectionObserver(可见性)
    • 与 `loading="lazy"` 选型
  • 六、`requestAnimationFrame`(rAF)
    • 与 `setTimeout`、微任务的分工
  • 七、了解即可(一笔带过)
  • 八、易混淆点归纳
  • 九、思考与练习
  • 总结

前言

DOM 主线分三篇收尾:第 16 篇讲节点与 API,第 17 篇讲渲染与性能;本篇补交互层——用户点击、滚动、元素进出视口时 JS 怎么响应。面试最常问的是事件委托;工程里还会用到IntersectionObserver(懒加载、加载更多)和requestAnimationFrame(动画/滚动)。CustomEventpassive等知道存在即可,不必单独成篇。


一、事件流:捕获与冒泡

DOM 事件传播:捕获(外→内)→ 目标 → 冒泡(内→外)。默认addEventListener冒泡阶段触发。

outer.addEventListener("click",()=>console.log("outer 冒泡"));outer.addEventListener("click",()=>console.log("outer 捕获"),true);btn.addEventListener("click",()=>console.log("btn"));// 点击 btn:outer 捕获 → btn → outer 冒泡

日常写业务冒泡阶段足够;捕获多用于拦截或框架内部,了解即可。


二、targetcurrentTarget

属性含义
event.target实际触发事件的元素(最内层,可能是文本节点)
event.currentTarget当前执行监听器的元素(addEventListener绑定的那个)

委托时二者不同:绑在父级#list上,currentTarget始终是#listtarget是具体子元素。

list.addEventListener("click",(e)=>{constli=e.target.closest("li");if(!li||!list.contains(li))return;console.log("点到 li:",li.dataset.id);});

closest(selector):从target向上找匹配祖先,避免点到span/文本节点时对不上li


三、事件委托(面试重点)

3.1 是什么

祖先元素上绑一个监听器,利用冒泡处理多个子孙(含后来插入的节点)的同类事件。

3.2 为什么用

  1. 动态列表:新增/删除项不用反复addEventListener/removeEventListener
  2. 监听器更少:内存与注册成本更低(第 17 篇)。
  3. 逻辑集中:列表、表格、菜单等结构清晰。
constlist=document.querySelector("#list");list.addEventListener("click",(e)=>{constitem=e.target.closest("li");if(!item)return;item.classList.toggle("active");});document.querySelector("#add").addEventListener("click",()=>{constli=document.createElement("li");li.textContent="新项";list.appendChild(li);// 无需再绑 click});

3.3 局限

  • 依赖冒泡focusmouseenter不冒泡,不能这样委托。
  • 需要closest过滤,避免误触嵌套结构。

四、不冒泡时的替代

不冒泡可冒泡替代
focus/blurfocusin/focusout
mouseenter/mouseleavemouseover/mouseout(会因子元素频繁触发)
form.addEventListener("focusin",(e)=>{if(e.target.matches("input"))e.target.classList.add("focused");});

scroll也不冒泡,需绑在滚动容器本身


五、IntersectionObserver(可见性)

异步观察元素与**视口(或 root 容器)**的交叉状态,常用于:

  • 图片懒加载(进入视口再设src
  • 无限滚动(底部哨兵进入视口加载下一页)
  • 曝光统计(元素可见比例达标上报)
constio=newIntersectionObserver((entries)=>{entries.forEach((entry)=>{if(!entry.isIntersecting)return;constimg=entry.target;img.src=img.dataset.src;io.unobserve(img);});},{rootMargin:"100px",threshold:0.01});document.querySelectorAll("img[data-src]").forEach((img)=>io.observe(img));
选项作用(知道即可)
root观察根,默认视口
rootMargin扩大/缩小触发区域,如提前 100px 加载
threshold可见比例 0~1 触发回调

loading="lazy"选型

loading="lazy"IntersectionObserver
成本原生,零 JS需写逻辑
控制浏览器决定rootMargin、回调自定义
场景普通<img>懒加载无限滚动哨兵、复杂曝光

建议:纯图片懒加载优先原生;加载更多、埋点用 IO。


六、requestAnimationFrame(rAF)

下一次重绘前执行回调,与显示器刷新率对齐(约 60fps),适合动画循环滚动中更新 UI

letticking=false;window.addEventListener("scroll",()=>{if(ticking)return;ticking=true;requestAnimationFrame(()=>{updateHighlight();// 读 scrollTop、改 class 等ticking=false;});});

setTimeout、微任务的分工

机制典型用途
微任务Promise.then异步结果、DOM 更新调度(第 08 篇)
setTimeout延迟、防抖定时
rAF视觉相关、跟帧动画/滚动

rAF不是精确定时器;后台标签页可能暂停或降频。精确计时应使用performance.now()+ 时间差,而非假设每帧 16ms。

动画属性优先transform/opacity(第 17 篇),在 rAF 里改它们更顺滑。


七、了解即可(一笔带过)

  • passive: true:告诉浏览器不会preventDefault,滚动更流畅;要阻止滚动需passive: false
  • once: true:监听一次后自动移除。
  • CustomEvent:DOM 节点上派发自定义事件,模块解耦;复杂场景可用 EventBus(第 11 篇)。
  • React / Vue:框架在根或元素上统一处理事件;原生addEventListener与框架onClick勿重复绑;卸载时记得清理。

八、易混淆点归纳

  1. 委托靠冒泡focus要用focusin
  2. targetcurrentTarget;委托看target+closest
  3. IO 不是 scroll 事件;不阻塞主线程滚动,回调异步触发。
  4. rAF ≠ 微任务;别用 rAF 替代Promise.then的语义。
  5. loading="lazy"与 IO互补,不是二选一排斥。

九、思考与练习

1.动态 Todo 列表为何推荐委托而不是每项onclick

解析:新增项不用绑事件;监听器O(1),维护简单。

2.点击li内文字,target可能是谁?如何拿到li

解析:可能是Text 节点或内层元素;e.target.closest('li')

3.图片首屏外懒加载,原生属性与 IO 如何选?

解析:简单<img loading="lazy">;要提前加载距离加载更多哨兵IO

4.滚动监听里直接做重 DOM 操作为何卡?rAF 起什么作用?

解析:scroll 触发极频繁;rAF合并到每帧一次,且对齐重绘。

5.form.addEventListener('focus', ...)能委托到所有 input 吗?

解析:不能focus不冒泡;用focusin


总结

  • 事件:捕获/冒泡;委托= 父级监听 + 冒泡 +closest,适合动态列表
  • 可见性IntersectionObserver做懒加载、无限滚动;简单图片可用loading="lazy"
  • rAF:动画/滚动对齐帧;与微任务、定时器分工不同
  • DOM 三篇(16 基础 / 17 性能 / 18 交互)至此收束。

下一阶段进入CSS 布局:居中、BFC、Flex/Grid 等(系列后续篇目)。

http://www.jsqmd.com/news/891016/

相关文章:

  • 3步拯救变砖Netgear路由器:NMRPFlash工具完全指南
  • 2026年5月福州闲置黄金变现攻略——从入门到不踩坑 - 润富黄金珠宝行
  • 自适应少样本提示:零数据撬动大模型,攻克低资源语言理解难题
  • Windows 11系统优化神器:Win11Debloat深度解析与实战指南
  • 野性重拟合:无需模型结构,评估复杂AI泛化能力的理论新工具
  • 基于影响函数的BPR推荐模型高效机器遗忘框架
  • Soul App协议逆向与SM4加密分析实战
  • 7步彻底解决Windows 11臃肿问题:Win11Debloat专业优化指南
  • 通用电子态密度预测模型PET-MAD-DOS:原理、架构与应用实践
  • HRT-ASC:Transformer优化框架,融合关系感知与自适应语义校准
  • 3个高效应用YOLOv5_OBB的实战技巧
  • 深度融合层:基于双耳信号与多任务学习的智能语音增强技术解析
  • OpenSSH CVE-2024-6387高危漏洞实战修复指南
  • Unity2D TileMap核心原理与运行时动态操作指南
  • 【核心机制】Browser-Use 是如何工作的?深度解析其独特的 DOM 向量化与坐标映射
  • UE5 DefaultLayout.ini 布局原理与 DockSpace 深度解析
  • 如何用ncbi-genome-download轻松获取基因组数据:从零开始的高效指南
  • 机器学习预测高熵合金硬度:LightGBM与BERT迁移学习实战对比
  • 基于情感嵌入与Transformer的多模态隐喻检测:从原理到工程实践
  • 国产多模态大模型数字人:从技术原理到产业未来全解析
  • CVE-2018-0886漏洞深度解析:CredSSP协议安全加固实战
  • 为什么你的Copilot+Notion+Make工作流总在第3天崩塌?,深度复盘127个失败案例中的4类隐性耦合断点
  • Winhance中文版:为Windows用户量身打造的系统优化大师
  • 残差注意力与高效上采样:提升遥感水体污染图像分类鲁棒性的工程实践
  • MulimgViewer:多图并行浏览的进阶实战指南
  • 5分钟搭建AI数字人对话系统:OpenAvatarChat完整指南
  • 如何5分钟永久激活Windows和Office:终极免费智能激活工具指南
  • 融合气象海洋数据,机器学习模型如何精准预测船舶油耗?
  • OpenAI教育计划限时开放!仅剩17天窗口期,如何用教育部学信网+国际院校双通道100%通过认证?
  • 学生党必藏:免费降AI率工具实测,论文过审攻略全整理