MutationObserver 是 DOM4 提供给前端同学的一个新的监控页面 DOM 变化的 API,以此来替换 DOM3 级的 Mutation events 这个过时的 API。通过这个 API 我们能做到页面上 DOM 属性节点文本的监控。

MutationObserver 翻译过来是突变观察者的意思,是一个接受回调参数的构造器。callback 回调函数会在指定的 DOM 节点(目标节点)发生变化时被调用。在调用时,观察者对象会传给该函数两个参数,第一个参数是个包含了若干个 MutationRecord 对象的数组,第二个参数则是这个观察者对象本身。

var observer = new MutationObserver( callback );  

MutationObserver 的原型上挂载了三个方法。

名称 作用
observe 给当前观察者对象注册需要观察的目标节点,在目标节点(还可以同时观察其后代节点)发生DOM变化时收到通知.
disconnect 让该观察者对象停止观察指定目标的DOM变化.直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用.
takeRecords 清空观察者对象的记录队列,并返回里面的内容.

MutationObserver 的一个优势是在于它所监控的 DOM callback 是异步的,并不是 dom 发生变化 MutationObserver 就会立刻出发。而是等到了当前的 dom 改变整个队列完成之后 MutationObserver 的回调才会生效。这无疑是增强了很大程度的性能优化。在前端页面状态变化如此快的发展背景下,MutationObserver 的优势就非常明显,热门 js 库 vue.js 中就用到了 MutationObserver。

一、如何使用?

MutationObserver 是一个构造函数,接收一个回调函数作为参数。构造函数的实例即是一个新的观察者对象。

var observer = new MutationObserver(callback);  

该回调函数会在指定的 DOM 节点(目标节点)发生变化时被调用。在调用时,观察者对象会传给该函数两个参数,第一个参数是个包含了若干个 MutationRecord 对象的数组,第二个参数则是这个观察者对象本身。

var observer = new MutationObserver(function(records, observer){  
    records.forEach(function(v, i){ console.log( v ); }); // 观察记录
    console.log( observer );                           // 观察对象
});

返回的实例对象上挂载了三个方法,分别是:

  • 1、observe 绑定需要观察的 dom 对象以及绑定配置。
  • 2、disconnect 让该观察者对象停止观察指定目标的 DOM 变化。
  • 3、takeRecords 清空观察者对象的记录队列,并返回里面的内容。

这三个方法用起来都很简单,首先来说第一个也是最常用的一个 observe。observe 的作用就是绑定需要观察的 dom 对象。observe 接收两个参数, 第一个是 node 节点也就是目标对象,第二个是初始的观察选项。options 包含了对于 dom 属性、子节点改变、文本节点是否监控的控制。

var observer = new MutationObserver(function(records, observer){
    records.forEach(function(v, i){ console.log( v ); });
    console.log( observer );
});

var target  = document.querySelector('#target');
var options = {
    'childList': true,              // 子节点
    'attributes': true,             // 节点属性
    'characterData': true,          // 文本节点、注释节点等
    'subtree': true,                // 所有该对象下的子孙节点
    'attributeOldValue': true,      // 在 attributes 为 true 的情况下,能够记录属性改变前的属性值
    'characterDataOldValue': true,  // 在 characterData 为 true 的情况下,能够记录改变前的文本内容
    'attributeFilter': []           // 一个特定的数组,表示只监控数组内的属性名。
};

observer.observe( target, options );

下面这个 DEMO 能够很方便的观察到 MutationObserver 所监控到的页面 dom 改变。

MutationObserver 测试节点

console

上面这个例子当中 MutationRecord 的 callback 第一个参数其实是一个包含了 MutationRecords 对象的数组。MutationRecords 对象有以下属性(摘抄 MDN):

属性 描述
type 如果是属性发生变化,则返回attributes.如果是一个CharacterData节点发生变化,则返回characterData,如果是目标节点的某个子节点发生了变化,则返回childList.
target 如果是属性发生变化,则返回attributes.如果是一个CharacterData节点发生变化,则返回characterData,如果是目标节点的某个子节点发生了变化,则返回childList.
addedNodes 返回被添加的节点,或者为null.
removedNodes 返回被删除的节点,或者为null.
previousSibling 返回被添加或被删除的节点的前一个兄弟节点,或者为null.
nextSibling 返回被添加或被删除的节点的后一个兄弟节点,或者为null.
attributeName 返回变更属性的本地名称,或者为null.
attributeNamespace 返回变更属性的命名空间,或者为null.
oldValue 根据type值的不同,返回的值也会不同.如果type为 attributes,则返回该属性变化之前的属性值.如果type为characterData,则返回该节点变化之前的文本数据.如果type为childList,则返回null.

disconnect 和 takeRecords 方法用起来很简单,就不在过多描述。

observer.disconnect();  // 切断监控  
observer.takeRecords(); // 清空当前的记录,并返回被清空的记录  

二、兼容性

MutationObserver 的兼容性还不错,至少主流的浏览器已经支持,使用前应该做好内核兼容。(图 2017-02-19)

MutationObserver 的兼容性

var MutationObserver       = window.MutationObserver  
                              || window.WebKitMutationObserver 
                              || window.MozMutationObserver;
var MutationObserverUsable = !!MutationObserver;  

除了 MutationObserver 老旧的浏览器实现了类似 MutationObserver 的 dom 监控方法。

document.addEventListener("DOMNodeInserted", function(e) {  
  console.log('插入了节点' + e.target.outerHTML)
}, false);
document.addEventListener("DOMNodeRemoved", function(e) {  
  console.log('删除了节点' + e.target.outerHTML)
}, false);

不过该方法在 IE9 上还是有 bug 的,在第一次插入的时候,不会触发监控的回调。不过 IE 这种还是让他们自己默默 go die 吧 ~