今天我们来学习如何使用 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 () {
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 () {
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平时使用的可能比较少,你用到过吗?
一起学习更多前端知识,微信搜索【小帅的编程笔记】,每天更新
|