摘要:在本教程中,您将学习如何使用 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 更改,并在每次更改发生时执行一个回调。