网站

MutationObserver | MDN

引入

有些页面内容不是自己直接修改的,而是由其他脚本、组件或第三方代码动态添加、删除或更新。此时如果需要在变化发生后执行额外操作,就需要有办法监听 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监听元素属性变化,例如 classsrc
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 事件循环 中的微任务执行顺序有关。