按照惯例,先看下MDN的介绍: MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
可以看到这个接口的作用是在DOM树修改之后提供了一个异步回调,方便用户自己处理DOM批量修改后的操作。因为是异步的,所以它会在所有DOM操作修改完后才调用回调方法,从而减少了调用次数。和事件不一样,事件是同步的。看下下面的一个例子:
<html>
<head></head>
<body>
<p id="pid" onclick="handler()">ywdong测试一下</p>
<script>
window.onload = function() {
// 选择需要观察变动的节点
var targetNode = document.querySelector('#pid')
// 观察器的配置(需要观察什么变动)
var config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
var callback = function(mutationsList, observer) {
// 当多次修改dom属性时,下面的console只打印一次
console.log('trigger callback')
for(let 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 if (mutation.type === 'subtree') {
console.log('The subtree was modified.');
}
}
}
// 创建一个观察器实例并传入回调函数
window.observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
window.observer.observe(targetNode, config);
// 之后,可停止观察
// window.observer.disconnect();
}
function handler() {
var obj = document.querySelector('#pid')
obj.setAttribute('name', 'testName')
obj.style.background = 'yellow'
var childNode = document.createTextNode("child node")
obj.appendChild(childNode)
}
</script>
</body>
</html>
看下点击结果
上面那个例子可以看到,我们多次修改了元素的属性,且新增了一个节点,但callback方法只执行了一次。如果每次修改都要执行回调的话,会显得效率很低下了。
config或者叫观察器的配置提供了以下几种,可以根据自身需要取不同的配置:
| 配置项 | 是否可选 | 含义 |
|---|---|---|
| attributeFilter | 可选 | 要监视的特定属性名称的数组。如果未包含此属性,则对所有属性的更改都会触发变动通知。无默认值。 |
| attributeOldValue | 可选 | 当监视节点的属性改动时,将此属性设为 true 将记录任何有改动的属性的上一个值。无默认值。 |
| attributes | 可选 | 设为 true 以观察受监视元素的属性值变更。默认值为 false。 |
| characterData | 可选 | 设为 true 以监视指定目标节点或子节点树中节点所包含的字符数据的变化。无默认值。 |
| characterDataOldValue | 可选 | 设为 true 以在文本在受监视节点上发生更改时记录节点文本的先前值。无默认值。 |
| childList | 可选 | 设为 true 以监视目标节点(如果 subtree 为 true,则包含子孙节点)添加或删除新的子节点。默认值为 false。 |
| subtree | 可选 | 设为 true 以将监视范围扩展至目标节点整个节点树中的所有节点。MutationObserverInit 的其他值也会作用于此子树下的所有节点,而不仅仅只作用于目标节点。默认值为 false。 |
这个API是在我学习微任务的时候了解的,那么这个时候也就更加了解为什么它是微任务了,因为它跟在UI Render后触发。而UI Render是宏任务。只有当前主线程中的宏任务执行完了才会执行微任务。