MutationObserver 接口

DOM规范中的 MutationObserver 接口可以在DOM被修改时异步执行回调。

使用MutationObserver可以观察整个文档、DOM树的一部分或某个元素,元素属性、字节点、文本等。

新引进的MutationObserver接口取代了已废弃的MutationEvent。

MutationObserver 实例

MutationObserver实例通过调用MutationObserver构造函数并传入一个回调函数创建。

新创建的MutationObserver实例,需要使用observe()方法与DOM进行关联。

observe()

两个必填参数:

要观察的DOM节点

MutationObserveInit 对象

MutationObserveInit对象用于控制观察哪些方面的变化,是一个键值对形式配置选项的字典。

回调与 MutationRecord

MutationObserver(构造函数)中的参数(回调函数),每个回调都会收到一个MutationRecord实例的数组(第一个参数),记录发生了什么变化以及DOM的哪部分收到了影响。

因为回调执行之前可能同时发生多个满足观察条件的事件,所以每次执行回调都会传入一个包含按顺序入队的MutationRecord实例的数组。

new MutationObserver(fn)

参数:回调函数fn。

回调函数fn的参数:

第一个参数:MutationRecord实例的数组

第二个参数:观察变化的MutationObserver的实例

MutationObserver 的回调函数可以多次执行。每当观察到 DOM 树的变化时,回调函数就会被调用。这些变化包括但不限于节点的添加、删除、属性的更改等。

那么问题来了

本例中,为什么回调中的 console.log 只输出了一次?

let ob = new MutationObserver(function () {console.log("%cbody attributes changed", "color: red; font-weight:bold;font-size: 20px;");
});
ob.observe(document.body, { attributes: true });
document.body.setAttribute("foo", "bar");
document.body.className = "newClass";

解答(来自通义灵码)

1.MutationObserver 是异步执行的

  • MutationObserver 的回调是通过 微任务队列(microtask queue) 异步触发的。
  • 当你在短时间内连续修改 DOM 属性时,浏览器会将这些变更合并为一个批次,并只调用一次回调函数

2. 属性变更被合并处理

  • 在你的代码中:

    document.body.setAttribute("foo", "bar"); document.body.className = "newClass";

    这两个操作都是对 document.body 的属性进行修改。
  • 因为它们在同一个事件循环中发生,所以 MutationObserver 只会收到一次“属性变化”的通知,并触发一次回调。

🧪 实验验证

如果你希望看到两次日志输出,可以使用 setTimeout 将这两个修改分开到不同的事件循环中:

document.body.setAttribute("foo", "bar"); setTimeout(() => { document.body.className = "newClass"; }, 0);

这样,MutationObserver 会在两个独立的 microtask 中检测到两次属性变更,从而触发两次回调。


✅ 总结

现象原因
console.log 只输出一次浏览器将多个 DOM 修改合并为一次变更通知,回调只执行一次
关键点说明
异步机制MutationObserver 使用微任务队列处理回调
批量优化浏览器自动合并多次变更以提升性能
控制输出次数可通过 setTimeout 或其他方式分隔变更事件

🛠️ 建议改进代码

// 分开两次 DOM 操作,确保触发两次回调 document.body.setAttribute("foo", "bar"); setTimeout(() => { document.body.className = "newClass"; }, 0);

这样就能在控制台看到两次 "body attributes changed" 的日志输出。

再记录一个通用示例

部分代码来自夸克浏览器。

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>木木的学习笔记</title><style>#target {border: 1px solid black;padding: 10px;margin-top: 10px;}</style></head><body><div class="container"><div class="navBox"></div><p>MutationObserver 的回调函数可以多次执行。每当观察到 DOM 树的变化时,回调函数就会被调用。这些变化包括但不限于节点的添加、删除、属性的更改等。</p><p>测试</p><div id="target">这是一个目标元素。</div><br /><button onclick="changeText()">改变文本</button></div><script src="./js/nav.js"></script><script>//加载页面导航var navObj = {title: "MutationObserver",chapter: "",section: "",part: "",};setNav(navObj);</script><script>// 选择需要观察变动的节点const targetNode = document.getElementById("target");// 配置观察选项const config = { childList: true, subtree: true, attributes: true, characterData: true };// 创建一个回调函数,当观察到DOM变动时会被调用const callback = function (mutationsList, observer) {console.log("DOM changed");for (let mutation of mutationsList) {if (mutation.type === "childList") {console.log("A child node has been added or removed.");} else if (mutation.type === "attributes") {console.log(`The ${mutation.attributeName} attribute was modified.`);} else if (mutation.type === "characterData") {console.log("Character data in the node has been changed.");}}};// 创建一个观察器实例并传入回调函数const observer = new MutationObserver(callback);// 开始观察目标节点observer.observe(targetNode, config);// 函数用于改变目标元素的文本内容let count = 0;function changeText() {targetNode.textContent = `文本已被第 ${++count} 次改变!`;}</script></body>
</html>

这个示例中,每次修改都被记录。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/89195.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89195.shtml
英文地址,请注明出处:http://en.pswp.cn/web/89195.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.7 小结

图3-7-1点云可视化点云可视化工具就像是打开点云数据宝藏大门的钥匙&#xff0c;能让我们直观地理解和分析这些复杂的数据。本章节&#xff0c;主要介绍了PCL、Open3D、Matplotlib、PCShow、VTK 这几种点云可视化工具。PCL&#xff08;Point Cloud Library&#xff09;是专注于…

对称二叉树、二叉树直径

101. 对称二叉树 - 力扣&#xff08;LeetCode&#xff09; 法一&#xff1a;递归。 对于两个对称位置的节点L和R&#xff08;L在左子树&#xff0c;R在右子树&#xff09;&#xff0c;只有当L的左节点值R的右节点值且L的右节点值R的左节点值时&#xff0c;这棵二叉树才有可能对…

Java多线程1

线程是操作系统能够运行调度的最小单位&#xff0c;它包含在进程之中&#xff0c;是进程的实际运作单位多线程有三种实现方式线程实现方法1&#xff0c;继承Thread类&#xff08;无返回值&#xff09;&#xff1a;1、继承Thread2、重写run方法&#xff08;线程要执行的代码&…

云计算如何提高企业的数据安全性和隐私保护

在企业数字化转型加速推进的今天&#xff0c;数据安全与隐私保护已成为决定企业生存发展的核心命题。云计算凭借其灵活的架构优势&#xff0c;不仅重塑了企业资源管理模式&#xff0c;更在数据安全防护领域构建起多层次保障体系。以下从六大维度解析云计算如何为企业数据安全与…

GaussDB 数据库架构师修炼(二)数据库计算容量评估

1 计算资源容量评估主要流程 一般地是经过以下5个流程评估GaussDB的计算容量: 2 TPC-C基准测试介绍 1)TPC-C是业界常用的一套Benchmark 由TPC (Transaction Processing Performance Council)委员会制定发布,用于 评测数据库的联机交易处理(偏向OLTP)能力,测试结果数据…

开源 python 应用 开发(六)网络爬虫

最近有个项目需要做视觉自动化处理的工具&#xff0c;最后选用的软件为python&#xff0c;刚好这个机会进行系统学习。短时间学习&#xff0c;需要快速开发&#xff0c;所以记录要点步骤&#xff0c;防止忘记。 链接&#xff1a; 开源 python 应用 开发&#xff08;一&#xf…

flink sql读hive catalog数据,将string类型的时间戳数据排序后写入kafka,如何保障写入kafka的数据是有序的

在 Flink SQL 中&#xff0c;要确保从 Hive 读取的 STRING 类型时间戳数据排序后有序写入 Kafka&#xff0c;需要结合 批处理模式、时间类型转换、单分区写入 和 Kafka 生产者配置。以下是完整解决方案&#xff1a; 一、核心解决方案 1. 批处理模式 全局排序 将作业设置为批处…

7.17 滑动窗口 |assign |memo

lcp56. memo优化tle或者改用bfsclass Solution {int m, n;int dx[4] {0, 0, 1, -1};int dy[4] {1, -1, 0, 0};public:int conveyorBelt(vector<string>& matrix, vector<int>& start, vector<int>& end) {int ret INT_MAX;m matrix.size();n…

统计功效是什么?

统计功效的通俗理解可以把“统计功效”想象成侦探破案的能力——它代表统计检验&#xff08;侦探&#xff09;在犯罪事实确实存在&#xff08;真实效应存在&#xff09;时&#xff0c;成功发现真相&#xff08;检测出效应&#xff09;的概率。核心比喻假设你是一个侦探&#xf…

大语言模型(LLM)训练的教师强制(Teacher Forcing)方法

大语言模型&#xff08;LLM&#xff09;在训练时使用一种名为“教师强制&#xff08;Teacher Forcing&#xff09;”的方法&#xff0c;而不是它们在推理&#xff08;生成文本&#xff09;时使用的“自回归&#xff08;Autoregressive&#xff09;”方法 。阐明关于LLM训练的一…

归一化与激活函数:深度学习的双引擎

归一化和激活函数区别 归一化和激活函数是深度学习中两个不同但又存在关联的技术,前者聚焦于“数据分布的调整”,后者聚焦于“引入非线性与输出转换”。 Softmax 既可以被视为一种归一化操作,也属于激活函数 因为它同时满足两者的核心特征,只是从不同角度定义:从“输出…

C# --- 单例类错误初始化 + 没有释放资源导致线程泄漏

C# --- 单例类错误初始化 没有释放资源导致线程泄漏Background原因分析问题一&#xff1a; 错误初始化&#xff08;使用了箭头函数&#xff09;问题一&#xff1a; 没有Dispose资源Background 背景: service A的其中一个Api会向mq发送消息问题&#xff1a;线上发现这个服务经常…

MySQL基础学习之DML,DQL(二)

这里写目录标题一、DML1、INSERT语句1)、给指定列添加数据2)、给全部列添加数据3)、批量数据添加数据4)、操作2、UPDATE语句3、DELETE语句二、DQL1、单表查询1&#xff09;查询语法2&#xff09;查询全部3&#xff09;查询部分4&#xff09;条件查询5&#xff09;聚合函数6&…

在 Linux 系统中实现 Spring Boot 程序自动启动的最佳实践

在实际部署 Spring Boot 项目的生产环境中&#xff0c;如何确保服务自动启动&#xff08;如开机自动运行、宕机自动恢复&#xff09;是一项基础而关键的运维能力。本文将系统介绍如何在 Linux 中将 Spring Boot 应用注册为 systemd 服务&#xff0c;实现进程守护与自动启动。&a…

如何建立项目团队的自驱力文化?

建立项目团队的自驱力文化&#xff0c;关键在于赋权机制、目标共创、持续反馈、内在激励、价值认同。 其中&#xff0c;“目标共创”尤其重要。项目成员若未参与目标制定&#xff0c;仅被动接受任务&#xff0c;将很难激发责任感和参与热情。反之&#xff0c;通过共创目标&…

【React Native】布局文件-底部TabBar

布局文件-底部tabBar 内容配置 export default function Layout() {return (<Tabs />); }默认会将布局文件是将与它在同一个目录的所有文件&#xff0c;包括下级目录的文件&#xff0c;全都配置成Tab了。&#xff1a; 这样做显然不对&#xff0c;正确的做法是 在app目…

CompareFace使用

CompareFace 使用 CompareFace 有三种服务&#xff0c;分别是人脸识别&#xff08;RECOGNITION&#xff09;、人脸验证&#xff08;VERIFICATION&#xff09;、人脸检测&#xff08;DETECTION&#xff09;。 人脸识别其实就是人脸身份识别(每张照片只有一个人脸)&#xff0c;…

APP测试之Monkey压力测试

&#xff08;一&#xff09;Monkey简介 Monkey意指猴子&#xff0c;顽皮淘气。所以Monkey测试&#xff0c;顾名思义也就像猴子一样在软件上乱敲按键&#xff0c;猴子什么都不懂&#xff0c;就爱捣乱。 Monkey 是 Android SDK 自带的命令行工具&#xff0c;它通过向系统发送伪…

时序大模型为时序数据库带来的变革与机遇

时序数据&#xff08;Time Series Data&#xff09;作为记录系统状态随时间变化的重要数据类型&#xff0c;在物联网、金融交易、工业监控等领域呈爆炸式增长。传统时序数据库专注于高效存储和查询时序数据&#xff0c;而时序大模型&#xff08;Time Series Foundation Models&…

深入核心:理解Spring Boot的三大基石:起步依赖、自动配置与内嵌容器

深入核心&#xff1a;理解Spring Boot的三大基石&#xff1a;起步依赖、自动配置与内嵌容器 摘要&#xff1a;在上一章&#xff0c;我们领略了Spring Boot带来的革命性开发体验。但魔法的背后&#xff0c;必有其科学的支撑。本章将带你深入Spring Boot的内核&#xff0c;系统性…