1. 概述
MutationObserver是DOM3 Events规范的一部分,该接口提供了监视DOM树变更的能力,通常用于监视DOM节点class、属性等内容的变更
2. MutationObserver构造函数
MutationObserver() 构造函数——是 MutationObserver 接口内容的一部分——创建并返回一个新的观察器,语法如下
var observer = new MutationObserver(callback);
接收的参数为回调函数callback,每当被指定的节点或子树以及配置项有Dom变动时会被调用。回调函数拥有两个参数:一个是描述所有被触发改动的 MutationRecord 对象数组,另一个是调用该函数的MutationObserver 对象
我们可以通过第一个参数获取到发生变更的DOM节点的信息以及变更的信息,如下所示:
function callback(mutationList, observer) {
mutationList.forEach((mutation) => {
switch(mutation.type) {
case 'childList':
/* 从树上添加或移除一个或更多的子节点;参见 mutation.addedNodes 与
mutation.removedNodes */
break;
case 'attributes':
/* mutation.target 中某节点的一个属性值被更改;该属性名称在 mutation.attributeName 中,
该属性之前的值为 mutation.oldValue */
break;
}
});
}
使用 mutation.type获取发生的变动类别(无论是子节点的变动,还是节点属性的变动)。
3. observe方法
var observer = new MutationObserver(callback);
前面我们通过new MutationObserver(callback)获得了观察者observer对象了,此时可以开始监听DOM节点变化,通过下面的语句来开启该行为
observer.observe(target, options)
观察者对象上具有observe方法,接收2个参数。第1个参数target表示要观察变化的DOM节点,第2个参数options是可选的MutationObserverInit对象,该对象通过一些配置描述了被观察的DOM的哪些变化要触发当前观察者对象的callabck,具体示例如下:
const dom = document.querySelector('.container');
observer.observe(dom, {
attributes: true,
childList: true
})
这段代码表示要观察指定对象的属性变化和子节点变化(增加或删除子节点),当触发了这2个变化时,执行上方初始化观察者示例时传入的callback方法。
第2个参数除了可配置attributes(是否监听属性变化) 和 childList(是否监听子节点增加/删除)外,还支持下面这些选项
4. MutationObserverInit
observe方法的第2参数,还支持下面配置
attributeFilter
要监视的特定属性名称的数组。如果未包含此属性,则对所有属性的更改都会触发变动通知。
attributeOldValue
当监视节点的属性改动时,将此属性设为 true 将记录任何有改动的属性的上一个值 (通过MutationRecord对象的oldValue获取到上一个值)。当不指定该选项或设置为false则不会记录上一个值(通过MutationRecord对象的oldValue获取到null)
attributes
设为 true 以观察受监视元素的属性值变更。默认值为 false。
characterData
设为 true 以监视指定目标节点或子节点树中节点所包含的字符数据的变化。无默认值。
characterDataOldValue
设为 true 以在文本在受监视节点上发生更改时记录节点文本的先前值。
childList
设为 true 以监视目标节点(如果 subtree 为 true,则包含子孙节点)添加或删除新的子节点。默认值为 false。
subtree
设为 true 以将监视范围扩展至目标节点整个节点树中的所有节点。MutationObserverInit 的其他值也会作用于此子树下的所有节点,而不仅仅只作用于目标节点。默认值为 false。
5. disconnect
在需要观测的逻辑已经执行完毕,不需要继续观测了,可以通过disconnect来阻止观察者继续接收消息
observer.disconnect()
直到再次调用其observe方法之前,该观察者对象包含的回调函数都不会再被调用。
6. 完整的使用案例
HTML部分
<div id="container" class="test-a"></div>
<button id="btn">Click</button>
<button id="disconnect">Disconnect</button>
JS部分
// 选择需要观察变动的节点
const targetNode = document.getElementById('container');
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
const { addedNodes, removedNodes } = mutation;
if (addedNodes.length) {
// 增加了子节点
console.log(`A ${addedNodes[0].nodeName} with text '${addedNodes[0].textContent}' has been added`);
}
if (removedNodes.length) {
// 移除了子节点
console.log(`A ${removedNodes[0].nodeName} has been removed`);
}
} else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
});
// 以上述配置开始观察目标节点,并传入观察的配置(需要观察哪些变更)
observer.observe(targetNode, {
attributes: true,
childList: true,
subtree: true,
});
let num = 1;
let p = null;
const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
if (num === 1) {
targetNode.setAttribute('test-val', '1');
}
if (num === 2) {
targetNode.classList.add('test-b');
}
if (num === 3) {
p = document.createElement('p');
p.innerText = 'OK!';
targetNode.appendChild(p);
}
if (num === 4) {
targetNode.removeChild(p);
}
num++;
})
const disconnectBtn = document.querySelector('#disconnect');
// 当完成所需的操作不需要继续观察了,调用disconnect方法取消观察
disconnectBtn.addEventListener('click', () => { observer.disconnect(); });
运行该页面后,在页面上的按钮点击3次,就可以看到控制台上输出了这样的4条日志
前面2条日志分别是修改了被观察节点的属性以及class触发的,第3条日志是往被观察元素中增加了DOM节点触发的,第4条日志是从元素中移除节点触发的
注意我们通过callback中接收的第1个参数mutationList可以获得变动的各种信息,包括变动的类型(mutation.type)、变动的属性名(mutation.attributeName)、塞入的节点(mutation.addedNodes)等等