今天我们来学习如何使用 JavaScript的 MutationObserver
API 来监听对 DOM 树所做的更改。
MutationObserver 简介
MutationObserver
API 允许我们监视对 DOM 树所做的更改。当 DOM 节点发生变化时,我们可以调用回调函数对变化做出反应。
使用 MutationObserver
API 的基本步骤是:
首先,定义DOM变化时将执行的回调函数:
function callback(mutations) {
//
}
其次,创建一个 MutationObserver
对象并将回调传给 MutationObserver()
构造函数:
let observer = new MutationObserver(callback);
然后,调用observe()
方法开始观察DOM变化。
observer.observe(targetNode, observerOptions);
observe()
方法有两个参数。target
是要监视更改的节点的根元素。 observerOptions
参数包含指定将哪些 DOM 更改报告给观察者的回调属性。
最后,通过调用 disconnect()
方法停止观察 DOM 变化:
observer.disconnect();
observerOptions 参数
observe()
方法的第二个参数让我们指定 MutationObserver
的参数:
let options = {
childList: true,
attributes: true,
characterData: false,
subtree: false,
attributeFilter: ['attr1', 'attr2'],
attributeOldValue: false,
characterDataOldValue: false
};
我们不需要使用所有属性。但是,要使 MutationObserver
正常工作,至少需要将 childList
、attributes
或 characterData
之一设置为 true
,否则 observer()
方法将会报错。
观察子元素变化
老规矩,还是假设有一个普普通通的列表:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MutationObserver示例: 子元素</title>
</head>
<body>
<ul id="language">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
<li>TypeScript</li>
</ul>
<button id="btnStart">开始观察</button>
<button id="btnStop">停止观察</button>
<button id="btnAdd">添加元素</button>
<button id="btnRemove">删除最后一个元素</button>
</body>
</html>
下面的例子说明了如何使用mutation options
对象的childList
属性来监控子节点的变化。
然后查找到所有按钮元素,停止观察按钮为禁用状态。
// 查找列表元素
let list = document.querySelector('#language');
// 查找按钮元素
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
其次,声明一个 log()
函数,该函数将用作 MutationObserver
的回调:
function log(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
console.log(mutation);
}
}
}
第三,创建一个新的 MutationObserver
对象:
let observer = new MutationObserver(log);
第四,在options
对象的childList
设置为true
的情况下,通过调用observe()
方法,在开始观察
按钮被点击时,开始观察列表元素子节点的DOM变化:
btnStart.addEventListener('click', function () {
observer.observe(list, {
childList: true
});
btnStart.disabled = true;
btnStop.disabled = false;
});
五、点击添加元素
按钮时添加一个新的li
列表项:
let counter = 1;
btnAdd.addEventListener('click', function () {
// 创建一个新的li元素
let item = document.createElement('li');
item.textContent = `子元素 ${counter++}`;
// 追加到列表的末尾
list.appendChild(item);
});
六、当点击删除最后一个元素
按钮时,删除列表的最后一个子元素:
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('没有子元素了');
});
最后,通过调用 MutationObserver
对象的 disconnect()
方法,在点击停止观察
按钮时停止观察对 DOM 更改:
btnStop.addEventListener('click', function () {
observer.disconnect();
// 改变按钮状态
btnStart.disabled = false;
btnStop.disabled = true;
});
最后贴上全部代码:
(function () {
// 查找列表元素
let list = document.querySelector('#language');
// 查找按钮元素
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
function log(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
console.log(mutation);
}
}
}
let observer = new MutationObserver(log);
btnStart.addEventListener('click', function () {
observer.observe(list, {
childList: true
});
btnStart.disabled = true;
btnStop.disabled = false;
});
btnStop.addEventListener('click', function () {
observer.disconnect();
// 改变按钮状态
btnStart.disabled = false;
btnStop.disabled = true;
});
let counter = 1;
btnAdd.addEventListener('click', function () {
// 创建一个新的li元素
let item = document.createElement('li');
item.textContent = `子元素 ${counter++}`;
// 追加到列表的末尾
list.appendChild(item);
});
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('没有子元素了');
});
})();
我们将所有代码放在一个 IIFE(立即调用函数表达式)中。防止对全局变量的污染。
点击此链接查看在线示例:https://codepen.io/cmdfas-the-bashful/pen/jOLxazY?editors=1010
当我们点击添加元素
按钮可以看到浏览器控制台中的打印:
观察属性的变化
要观察属性的更改,请使用下面的options
对象属性:
let options = {
attributes: true
}
如果想要观察一个或多个指定属性的变化而忽略其他属性,可以使用 attributeFilter
属性:
let options = {
attributes: true,
attributeFilter: ['class', 'style']
}
在此示例中,每次类或样式属性更改时,MutationObserver
都会调用回调。
观察目标节点及子节点
要观察目标节点及其子节点DOM树,将options
对象的subtree
属性设置为 true:
let options = {
subtree: true
}
观察文本内容变化
要观察节点的文本内容更改,将options
对象的characterData
属性设置为 true:
let options = {
characterData: true
}
访问旧值
要访问属性的旧值,将options
对象的 attributeOldValue
属性设置为 true:
let options = {
attributes: true,
attributeOldValue: true
}
同样,可以通过将 options
对象的 characterDataOldValue
属性设置为 true 来访问文本内容的旧值:
let options = {
characterData: true,
subtree: true,
characterDataOldValue: true
}
最后
MutationObserver API平时使用的可能比较少,你用到过吗?
一起学习更多前端知识,微信搜索【小帅的编程笔记】,每天更新