MutationObserver
接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
MutationObserver兼容性,MutationObserver
构造函数
MutationObserver()创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。
~注意:MutationObserver只能监测到诸如属性改变、增删子结点等,对于自己本身被删除,是没有办法的, 但是可以通过监测父结点来达到要求。~
方法
disconnect()
阻止 MutationObserver 实例继续接收通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。
observe()
配置MutationObserver
在DOM更改匹配给定选项时,通过其回调函数开始接收通知.
takeRecords()
从MutationObserver
的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord
对象的新Array
中.它的一个作用是,比如你对一个节点的操作你不想马上就做出反应,过段时间在显示改变了节点的内容。
var observe=new MutationObserver(function(){});
observe.observe(target,{ childList: true});
target.appendChild(document.createTextNode('新增Text节点'));
var record = observe.takeRecords(); //此时record保存了改变记录列表
//当调用takeRecords方法时,记录队列被清空因此不会触发MutationObserver中的callback回调方法。
target.appendChild(document.createElement('span'));
observe.disconnect(); //停止对target的观察。
//MutationObserver中的回调函数只有一个记录,只记录了新增span元素
//之后可以对record进行操作
//...
MutationObserver 的实例的observe方法用来启动监听,它接受两个参数。
第一个参数:所要观察的 DOM 节点,第二个参数:一个配置对象,指定所要观察的特定变动,有以下几种:
属性 | 描述 |
---|---|
childList | 如果需要观察目标节点的子节点(新增了某个子节点,或者移除了某个子节点),则设置为true. |
attributes | 如果需要观察目标节点的属性节点(新增或删除了某个属性,以及某个属性的属性值发生了变化),则设置为true. |
characterData | 如果目标节点为characterData节点(一种抽象接口,具体可以为文本节点,注释节点,以及处理指令节点)时,也要观察该节点的文本内容是否发生变化,则设置为true. |
subtree | 除了目标节点,如果还需要观察目标节点的所有后代节点(观察目标节点所包含的整棵DOM树上的上述三种节点变化),则设置为true. |
attributeOldValue | 在attributes属性已经设为true的前提下,如果需要将发生变化的属性节点之前的属性值记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true. |
characterDataOldValue | 在characterData属性已经设为true的前提下,如果需要将发生变化的characterData节点之前的文本内容记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true. |
attributeFilter | 一个属性名数组(不需要指定命名空间),只有该数组中包含的属性名发生变化时才会被观察到,其他名称的属性发生变化后会被忽略. |
MutationObserver的callback回调函数是异步的,只有在全部DOM操作完成之后才会调用callback。
2.当只设置{ childList: true}时,表示观察目标子节点的变化
如果想要观察到子节点以及后代的变化需设置{childList: true, subtree: true}
attributes选项用来观察目标属性的变化,用法类似与childList,目标属性的删除添加以及修改都会被观察到。
3.我们需要注意的是characterData这个选项,它是用来观察CharacterData类型的节点的,只有在改变节点数据时才会观察到,如果你删除或者增加节点都不会进行观察,还有如果对不是CharacterData类型的节点的改变不会观察到
observe.observe(target,{ characterData: true, subtree: true});
target.childNodes[0].textContent='改变Text节点'; //观察到
target.childNodes[1].textContent='改变p元素内容'; //不会观察到
target.appendChild(document.createTextNode('新增Text节点')); //不会观察到
target.childNodes[0].remove(); //删除TEXT节点也不会观察到
我们只需要记住只有对CharacterData类型的节点的数据改变才会被characterData为true的选项所观察到。
4.最后关注一个特别有用的选项attributeFilter,这个选项主要是用来筛选要观察的属性,比如你只想观察目标style属性的变化,这时可以如下设置:
observe.observe(target,{ attributeFilter: ['style'], subtree: true});
target.style='color:red'; //可以观察到
target.removeAttribute('name'); //删除name属性,无法观察到
MutationRecord
变动记录中的属性如下:
1.type:如果是属性变化,返回"attributes",如果是一个CharacterData节点(Text节点、Comment节点)变化,返回"characterData",节点树变化返回"childList"
2.target:返回影响改变的节点
3.addedNodes:返回添加的节点列表
4.removedNodes:返回删除的节点列表
5.previousSibling:返回分别添加或删除的节点的上一个兄弟节点,否则返回null
6.nextSibling:返回分别添加或删除的节点的下一个兄弟节点,否则返回null
7.attributeName:返回已更改属性的本地名称,否则返回null
8.attributeNamespace:返回已更改属性的名称空间,否则返回null
9.oldValue:返回值取决于type。对于"attributes",它是更改之前的属性的值。对于"characterData",它是改变之前节点的数据。对于"childList",它是null其中 type、target这两个属性不管是哪种观察方式都会有返回值,其他属性返回值与观察方式有关,比如只有当attributeOldValue或者characterDataOldValue为true时oldValue才有返回值,只有改变属性时,attributeName才有返回值等。
示例
The following example was adapted from this blog post.
// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');
// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true, subtree: true };
// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
for(var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}else{
console.log('The ' + mutation.attributeName + ' subtree was modified.');
}
}
};
// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
observer.disconnect();
完整示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>mutation observer</title>
</head>
<body>
<div id="app">
<h2>
监视dom树变化
</h2>
<div id="deleteId">
看我的颜色
</div>
</div>
<script>
var targetNode = document.getElementById("app");
var config = {
attributes: true,
childList: true,
subtree: true
};
var callback = function (mutationsList) {
for (var mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
} else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}else{
console.log('The ' + mutation.attributeName + ' subtree was modified.');
}
}
};
let observer = new MutationObserver(callback);
observer.observe(targetNode, config);
// later , you can stop observing
//observer.disconnect();
setTimeout(() => {
var dom = document.getElementById("deleteId");
dom.style.color = "red";
}, 1000);
setTimeout(() => {
var dom = document.getElementById("deleteId");
dom.style.fontSize = '20px';
}, 2000);
setTimeout(() => {
var dom = document.getElementById("deleteId");
document.getElementById('app').removeChild(dom);
}, 5000);
</script>
</body>
</html>
ES6 语法
// Select the node that will be observed for mutations
let targetNode = document.querySelector(`#id`);
// Options for the observer (which mutations to observe)
let config = {
attributes: true,
childList: true,
subtree: true
};
// Callback function to execute when mutations are observed
const mutationCallback = (mutationsList) => {
for(let mutation of mutationsList) {
let type = mutation.type;
switch (type) {
case "childList":
console.log("A child node has been added or removed.");
break;
case "attributes":
console.log(`The ${mutation.attributeName} attribute was modified.`);
break;
case "subtree":
console.log(`The subtree was modified.`);
break;
default:
break;
}
}
};
// Create an observer instance linked to the callback function
let observer = new MutationObserver(mutationCallback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
observer.disconnect();
使用场景:
检测dom变化动态执行方法, 检测dom变化重新渲染dom防止别人篡改,例如网页水印制作防止人为去掉dom水印等等场景.
参考连接:
https://segmentfault.com/a/1190000012787829
https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver