深入 MutationObserver

8,405 阅读4分钟
原文链接: fecoding.cn

MutationObserver 给开发者们提供了一种能在某个范围内的 DOM 树发生变化时作出适当反应的能力。该 API 设计用来替换掉在 DOM 3 事件规范中引入的 Mutation 事件。

——MDN

1. 概述

  • 监视 DOM 变动的接口

    当监视的 DOM 发生变动时 MutationObserver 将收到通知并触发事先设定好的回调函数。

  • 类似于事件,但是异步触发

    添加监视时,MutationObserver 上的 observer 函数与 addEventListener 有相似之处,但不同于后者的同步触发,MutationObserver 是异步触发,此举是为了避免 DOM 频繁变动导致回调函数被频繁调用,造成浏览器卡顿。

2. MutationObserver 构造函数

该构造函数用于实例化一个新的 MutaionObserver ,同时指定触发 DOM 变动时的回调函数:

JavaScript
var observer = new MutationObserver(callback);

callback,即回调函数接收两个参数,第一个参数是一个包含了所有 MutationRecord 对象(后文会介绍,别急)的数组,第二个参数则是这个MutationObserver 实例本身。

3. MutationObserver 实例方法

3.1 Observe(Node target, optional MutationObserverInit options);

给 MutationObserver 实例添加要观察的 DOM 节点,并可通过一个可选的 options 参数来配置观察哪些变动,该 options 为一个名为 MutationObserverInit 的对象。

以下是 MutationObserverInit 对象的各属性及其描述:

属性 类型 描述
childList Boolean 是否观察子节点的变动
attributes Boolean 是否观察属性的变动
characterData Boolean 是否节点内容或节点文本的变动
subtree Boolean 是否观察所有后代节点的变动
attributeOldValue Boolean 观察 attributes 变动时,是否记录变动前的属性值
characterDataOldValue Boolean 观察 characterData 变动时,是否记录变动前的属性值
attributeFilter Array 表示需要观察的特定属性(比如['class','src']),不在此数组中的属性变化时将被忽略

注:

  • 不能单独观察 subtree 变动,必须同时指定 childList、attributes 和 characterData 中的一种或多种。
  • 为同一个 DOM 节点多次添加同一个 MutationObserver 是无效的,回调函数将只被触发一次。但如果指定不同的 options 对象(即观察不同的变动),即被视为不同的 MutationObserver。(是不是感觉跟 addEventListener 确实很像?)

3.2  disconnect();

该方法用来停止观察。后续如果 DOM 节点发生变动将不再触发回调函数。

JavaScript
observer.disconnect();

继续类比,disconnect 函数和 removeEventListener大致类似,除了 disconnect 函数比较粗暴(无法传参配置),会把该 MutationObserver 实例上所有的观察都停止。

3.3  takeRecords();

该方法用来清除变动记录,返回一个包含了 MutationRecord 对象的数组。MutationRecord 又出现了。它是啥?

实际上,DOM 每次发生变化,就会生成一条变动记录,这个变动记录对应一个 MutationRecord 对象。以下是 MutationRecord 对象的各属性及其描述:

属性 类型 描述
type String 根据变动类型,值为 attributes, characterData 或 childList
target Node 发生变动的DOM节点
addedNodes NodeList 被添加的节点,或者为 null
removedNodes NodeList 被删除的节点,或者为 null
previousSibling Node 被添加或被删除的节点的前一个兄弟节点,或者为 null
nextSibling Node 被添加或被删除的节点的后一个兄弟节点,或者为 null
attributeName String 发生变更的属性的本地名称,或者为 null
attributeNamespace String 发生变更的属性的命名空间,或者为 null
oldValue String 如果 type 为 attributes,则返回该属性变化之前的属性值;如果 type 为 characterData,则返回该节点变化之前的文本数据;如果 type为 childList,则返回 null

前面我们说 MutationObserver 异步处理触发,实际上它的原理就是等待一个脚本任务完成,将这个过程中产生的所有变动记录即所有的 MutationRecord 对象存储到一个数组中,最后再统一处理这个数组。而这个包含了所有变动记录的数组,也将成为回调函数的第一个参数。下面是一个回调函数处理该数组的示例:

JavaScript
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

4. 兼容性

MutationObserver 在 DOM Level 4 中被引入,其兼容性如下图所示:

参考文献:

MDN MutationObserver

《JavaScript 标准参考教程(alpha)》—— MutationObserver

DOM JavaScript

坚持原创技术分享,您的支持将鼓励我继续创作!

扫描二维码,分享此文章