摘要:在本教程中,您将学习如何使用 JavaScript MutationObserver
API 监控对 DOM 树所做的更改。
JavaScript 变异观察器 API 简介
MutationObserver
API 允许您监控对 DOM 树所做的更改。当 DOM 节点发生变化时,您可以调用一个回调函数 来对这些变化做出反应。
使用 MutationObserver
API 的基本步骤是
首先,定义一个在 DOM 发生变化时执行的回调函数
function callback(mutations) {
//
}
Code language: JavaScript (javascript)
其次,创建一个 MutationObserver
对象,并将回调传递给 MutationObserver()
构造函数
let observer = new MutationObserver(callback);
Code language: JavaScript (javascript)
第三,调用 observe()
方法开始观察 DOM 变化。
observer.observe(targetNode, observerOptions);
Code language: JavaScript (javascript)
observe()
方法有两个参数。target
是要监控更改的节点子树的根。observerOptions
参数包含指定应向观察者的回调报告哪些 DOM 更改的属性。
最后,通过调用 disconnect()
方法停止观察 DOM 更改
observer.disconnect();
Code language: JavaScript (javascript)
变异观察器选项
observe()
方法的第二个参数允许您指定选项来描述 MutationObserver
let options = {
childList: true,
attributes: true,
characterData: false,
subtree: false,
attributeFilter: ['attr1', 'attr2'],
attributeOldValue: false,
characterDataOldValue: false
};
Code language: JavaScript (javascript)
您不需要使用所有选项。但是,为了使 MutationObserver
工作,至少需要将 childList
、attributes
或 characterData
中的一个设置为 true
,否则 observer()
方法将抛出错误。
观察对子元素的更改
假设您有以下列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MutationObserver Demo: ChildList</title>
</head>
<body>
<ul id="language">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
<li>TypeScript</li>
</ul>
<button id="btnStart">Start Observing</button>
<button id="btnStop">Stop Observing</button>
<button id="btnAdd">Add</button>
<button id="btnRemove">Remove the Last Child</button>
<script src="app.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
以下示例说明了如何使用变异 options
对象的 childList
属性来监控子节点的变化。
首先,使用 querySelector()
方法选择元素(如 list
和 buttons
)。默认情况下,停止观察
按钮处于 disabled
状态。
// selecting list
let list = document.querySelector('#language');
// selecting buttons
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
Code language: JavaScript (javascript)
其次,声明一个 log()
函数,该函数将用作 MutationObserver
的回调
function log(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
console.log(mutation);
}
}
}
Code language: JavaScript (javascript)
第三,创建一个新的 MutationObserver
对象
let observer = new MutationObserver(log);
Code language: JavaScript (javascript)
第四,当单击 开始观察
按钮时,开始观察列表元素子节点的 DOM 变化,方法是调用 observe()
方法,并将 options
对象的 childList
设置为 true
btnStart.addEventListener('click', function () {
observer.observe(list, {
childList: true
});
btnStart.disabled = true;
btnStop.disabled = false;
});
Code language: JavaScript (javascript)
第五,当单击 添加
按钮时,添加一个新的列表项
let counter = 1;
btnAdd.addEventListener('click', function () {
// create a new item element
let item = document.createElement('li');
item.textContent = `Item ${counter++}`;
// append it to the child nodes of list
list.appendChild(item);
});
Code language: JavaScript (javascript)
第六,当单击 移除
按钮时,移除 list
的最后一个子节点
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('No more child node to remove');
});
Code language: JavaScript (javascript)
最后,当单击 停止观察
按钮时,通过调用 MutationObserver
对象的 disconnect()
方法停止观察 DOM 更改
btnStop.addEventListener('click', function () {
observer.disconnect();
// set button states
btnStart.disabled = false;
btnStop.disabled = true;
});
Code language: JavaScript (javascript)
将所有内容放在一起
(function () {
// selecting the list
let list = document.querySelector('#language');
// selecting the buttons
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');
// disable the stop button
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();
// Set the button state
btnStart.disabled = false;
btnStop.disabled = true;
});
let counter = 1;
btnAdd.addEventListener('click', function () {
// create a new item element
let item = document.createElement('li');
item.textContent = `Item ${counter++}`;
// append it to the child nodes of list
list.appendChild(item);
});
btnRemove.addEventListener('click', function () {
list.lastElementChild ?
list.removeChild(list.lastElementChild) :
console.log('No more child node to remove');
});
})();
Code language: JavaScript (javascript)
请注意,我们将所有代码放在一个IIFE(立即调用函数表达式)中。
观察属性的变化
要观察属性的变化,您使用 options
对象的以下 attributes
属性
let options = {
attributes: true
}
Code language: JavaScript (javascript)
如果您想观察对一个或多个特定 attributes
的更改,同时忽略其他更改,可以使用 attributeFilter
属性
let options = {
attributes: true,
attributeFilter: ['class', 'style']
}
Code language: JavaScript (javascript)
在此示例中,MutationObserver
将在每次 class
或 style
属性发生更改时调用回调。
观察对子树的更改
要监控目标节点及其节点子树,您将 options
对象的 subtree
属性设置为 true
let options = {
subtree: true
}
Code language: JavaScript (javascript)
观察对字符数据的更改
要监控节点的文本内容的变化,您将 options
对象的 characterData
属性设置为 true
let options = {
characterData: true
}
Code language: JavaScript (javascript)
访问旧值
要访问属性的旧值,您将 options
对象的 attributeOldValue
属性设置为 true
let options = {
attributes: true,
attributeOldValue: true
}
Code language: JavaScript (javascript)
类似地,您可以通过将 options
对象的 characterDataOldValue
属性设置为 true
来访问字符数据的旧值
let options = {
characterData: true,
subtree: true,
characterDataOldValue: true
}
Code language: JavaScript (javascript)
变异观察器的实际示例
在 JavaScript 应用程序中,页面上的元素通常是动态生成的。要等待动态元素,您需要使用 MutationObserver
。
以下 waitForElement()
函数使用 MutationObserver
等待一个或多个由选择器指定的元素。
function waitForElement(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(element);
}
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
}
Code language: JavaScript (javascript)
它是如何工作的。
waitForElement()
函数返回一个 Promise。一旦元素可用,该 Promise 将被解析。
首先,如果元素可用,则解析元素
if (document.querySelector(selector)) {
return resolve(element);
}
Code language: JavaScript (javascript)
其次,如果元素不可用,则创建一个新的 MutationObserver
对象来观察 DOM 树
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
observer.disconnect();
}
});
Code language: JavaScript (javascript)
观察者对象将在元素可用时调用 resolve() 函数,并停止观察 DOM 树。
第三,观察整个 DOM 树的元素
observer.observe(document.body, {
childList: true,
subtree: true,
});
Code language: CSS (css)
因为 waitForElement()
返回一个 Promise
,所以您可以使用 then()
方法,如下所示
waitForElement()('.a-class').then((element) => {
console.log('Element is ready');
console.log(element.textContent);
});
Code language: JavaScript (javascript)
或者您可以使用 await
语法
const element = await waitForElement()('.a-class');
console.log(element.textContent);
Code language: JavaScript (javascript)
在本教程中,您已经了解了 JavaScript MutationObserver
API,它监控 DOM 更改,并在每次更改发生时执行一个回调。