MutationObserver 可用来监视 DOM 的变化,算是一个比较老的 API,但是却鲜为人知,他的前身是 MutationEvent:一系列监听 DOM 变更的 event 事件 - DOMAttrModified、DOMNodeInserted、DOMSubtreeModified 等。
个人能想到的使用场景一个是监听 DOM 的变化,防止用户篡改 DOM 结构,比如可以在被篡改后直接 document.write('请勿篡改') 来避免用户篡改重要页面并进行截图作秀。又或者是使用 contenteditable 时监听内容变更,在富文本编辑器领域可能会有所应用。还可以监听一些浏览器插件修改 DOM 节点的行为。
用法
与其它 Observer 类似,使用 MutationObserver 首先需要实例化一个 observer,然后调用它的 observe 来监听目标元素:
const observer = new MutationObserver((mutationList, observer) => {
mutationList.forEach(mutation => {
console.log(mutation);
});
});
observer.observe(target, { attributes: true, childList: true, subtree: true });
这里 observe 一定要通过传入 options 明确传入需要监听的改动类型等参数,其中要 childList、attributes、characterData 至少一个为 true:
childList: 表明是否监听子元素的增删attributes: 表明是否监听属性的变动attributesFilter: 用来声明需要监听的属性名称,如果传入了则attributes默认为trueattributeOldValue: 设置为true后可以在mutation信息中拿到attribute变动前的值characterData: 表明是否需要监听字符数据的变化,可以理解为文本节点的变化,注意添加删除文本节点触发的是childList,只有改的时候才会触发characterData变化characterDataOldValue: 同attributeOldValue类似,设置为true后可以在mutation信息中拿到characterData变化前的值subtree:subtree比较特殊,表示是否需要监听子元素的变动,子元素监听选项将会从此处option继承
回调中接受两个参数:mutationList 为变动的 mutation 事件列表,observer 还是老样子,上面 observer 实例的一个引用。
mutation 中字段较多,可以从中直接获得 DOM 变更的细节:
type: 标识本次变动的类型,包含三种类型,同上面option中的三种监听类型:attributes表示dom的属性变动characterData表示文本节点的变动childList表示其它的node节点变动
target: 当前mutation影响的DOM节点,三种类型下有些许区别:attributes类为属性变化的元素characterData类则为变动的文本节点childList类为变动的child的父元素
addedNodes: 为添加的节点removedNodes: 为删除的节点previousSibling: 删除或添加的节点的前一个兄弟节点nextSibling: 删除或添加的节点的后一个兄弟节点attributeName: 变动的属性名称attributeNamespace: 变动的属性的namespace,很少用到oldValue: 变动前的属性,内容视类型和配置中的oldValue相关配置决定
需要注意,mutationList 是所有改动事件的合集,比如一次性改动 n 个属性,将会生成 n 个 mutation。
其它 API
同样除了 observe 外,MutationObserver 也提供了 disconnect 来取消所有监听,但是居然没有提供 unobserve,emm。
observer 还提供了 takeRecords,用来获取所有已经发生的改动但是却还没进入回调处理的事件,一般用于在 disconnect 前处理还未被处理的改动。
总结
MutationObserver 在日常使用中场景非常少见,然而在特定场景下可以发挥出强大的作用。