DOM 变动观察器

74 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

前言

MutationObserver 是一个内建对象,它观察 DOM 元素,并在检测到更改时触发回调。

我们将首先看一下语法,然后探究一个实际的用例,以了解它在什么地方有用。

语法

MutationObserver 使用简单。

首先,我们创建一个带有回调函数的观察器:

let observer = new MutationObserver(callback);

然后将其附加到一个 DOM 节点:

observer.observe(node, config);

config 是一个具有布尔选项的对象,该布尔选项表示“将对哪些更改做出反应”:

  • childList —— node 的直接子节点的更改,
  • subtree —— node 的所有后代的更改,
  • attributes —— node 的特性(attribute),
  • attributeFilter —— 特性名称数组,只观察选定的特性。
  • characterData —— 是否观察 node.data(文本内容),

其他几个选项:

  • attributeOldValue —— 如果为 true,则将特性的旧值和新值都传递给回调(参见下文),否则只传新值(需要 attributes 选项),
  • characterDataOldValue —— 如果为 true,则将 node.data 的旧值和新值都传递给回调(参见下文),否则只传新值(需要 characterData 选项)。

然后,在发生任何更改后,将执行“回调”:更改被作为一个 MutationRecord 对象列表传入第一个参数,而观察器自身作为第二个参数。

MutationRecord 对象具有以下属性:

  • type —— 变动类型,以下类型之一:

    • "attributes":特性被修改了,
    • "characterData":数据被修改了,用于文本节点,
    • "childList":添加/删除了子元素。
  • target —— 更改发生在何处:"attributes" 所在的元素,或 "characterData" 所在的文本节点,或 "childList" 变动所在的元素,

  • addedNodes/removedNodes —— 添加/删除的节点,

  • previousSibling/nextSibling —— 添加/删除的节点的上一个/下一个兄弟节点,

  • attributeName/attributeNamespace —— 被更改的特性的名称/命名空间(用于 XML),

  • oldValue —— 之前的值,仅适用于特性或文本更改,如果设置了相应选项 attributeOldValue/characterDataOldValue

例如,这里有一个 <div>,它具有 contentEditable 特性。该特性使我们可以聚焦和编辑元素。

<div contentEditable id="elem">Click and <b>edit</b>, please</div>

<script>
let observer = new MutationObserver(mutationRecords => {
  console.log(mutationRecords); // console.log(the changes)
});

// 观察除了特性之外的所有变动
observer.observe(elem, {
  childList: true, // 观察直接子节点
  subtree: true, // 及其更低的后代节点
  characterDataOldValue: true // 将旧的数据传递给回调
});
</script>

如果我们在浏览器中运行上面这段代码,并聚焦到给定的 <div> 上,然后更改 <b>edit</b> 中的文本,console.log 将显示一个变动:

mutationRecords = [{
  type: "characterData",
  oldValue: "edit",
  target: <text node>,
  // 其他属性为空
}];

如果我们进行更复杂的编辑操作,例如删除 <b>edit</b>,那么变动事件可能会包含多个变动记录:

mutationRecords = [{
  type: "childList",
  target: <div#elem>,
  removedNodes: [<b>],
  nextSibling: <text node>,
  previousSibling: <text node>
  // 其他属性为空
}, {
  type: "characterData"
  target: <text node>
  // ...变动的详细信息取决于浏览器如何处理此类删除
  // 它可能是将两个相邻的文本节点 "edit " 和 ", please" 合并成一个节点,
  // 或者可能将它们留在单独的文本节点中
}];

因此,MutationObserver 允许对 DOM 子树中的任何更改作出反应。