网站
引入
有些页面内容不是自己直接修改的,而是由其他脚本、组件或第三方代码动态添加、删除或更新。此时如果需要在变化发生后执行额外操作,就需要有办法监听 DOM 的变化。
MutationObserver 就是用来观察 DOM 变化的 API。
正文
定义
定义
MutationObserver用于监听 DOM 树、元素属性或文本内容的变化。浏览器发现变化后,会异步调用事先传入的回调函数。
它不是某个节点上的属性。childList 也不是直接写成 element.childList 来读取的属性,而是传给 observer.observe() 的观察选项,表示是否关注子节点的增加和删除。
基础语法
先创建观察器,再指定要观察的元素和变化类型。
const observer = new MutationObserver(function (records) {
console.log(records);
});
observer.observe(target, {
childList: true
});target:要观察的目标节点records:本次变化的信息列表childList: true:观察子节点的增加和删除
观察子节点变化
下面的例子监听 <ul> 里的 <li> 是否被添加或删除。
<ul id="list"></ul>
<button id="addBtn">添加</button>const list = document.querySelector("#list");
const addBtn = document.querySelector("#addBtn");
const observer = new MutationObserver(function (records) {
records.forEach(function (record) {
console.log(record.addedNodes);
console.log(record.removedNodes);
});
});
observer.observe(list, {
childList: true
});
addBtn.addEventListener("click", function () {
const item = document.createElement("li");
item.textContent = "新的列表项";
list.appendChild(item);
});点击按钮后,list.appendChild(item) 会添加新的 <li>,观察器会收到一条记录。新增节点在 record.addedNodes 中,删除节点在 record.removedNodes 中。
常见观察选项
observer.observe() 的第二个参数决定关注哪些变化。
| 选项 | 作用 |
|---|---|
childList: true | 监听直接子节点的增加和删除 |
subtree: true | 连同所有后代节点一起监听 |
attributes: true | 监听元素属性变化,例如 class、src |
characterData: true | 监听文本节点内容变化 |
observer.observe(document.body, {
childList: true,
subtree: true
});这段代码会监听 <body> 内任意层级的节点增删。观察范围越大,回调可能越频繁,因此应尽量缩小目标范围。
停止观察
不再需要时,调用 disconnect() 停止观察,避免持续执行不必要的回调。
observer.disconnect();注意事项
MutationObserver监听的是 DOM 已经发生的变化,不能阻止变化发生- 回调不是在修改 DOM 的那一行立刻同步执行,而是会在当前同步代码结束后执行
childList只监听子节点增删,不监听属性和文本内容变化- 要监听后代节点,必须同时设置
subtree: true - 回调里继续修改正在观察的 DOM,可能再次触发观察器,要避免形成重复执行
- 页面销毁或不再使用时调用
disconnect()
理解
可以把 MutationObserver 理解为给一段 DOM 区域安排了一个观察员。
元素内部有节点增加、删除或属性变化时,观察员会在之后把变化记录交给回调函数处理。它适合处理自己无法直接控制、但又需要感知的页面变化。
如果变化就是自己写代码产生的,通常直接在修改 DOM 后继续执行下一步即可,不需要额外使用 MutationObserver。
引出
MutationObserver 的回调会异步执行,它和 JS 事件循环 中的微任务执行顺序有关。