我发现了 MutationObserver 接口,用于拦截和分析 DOM 中的变化。
它工作得很好,我已经成功地拦截了所有 DOM 更改...除了使用 innerHTML
节点时通过
documentElement
方法进行的 DOM更改。
这是我的代码:
function test() {
const config = {
subtree: true,
childList: true
};
const callback = (mutationList) => {
for (const mutation of mutationList) {
if (mutation.type === "childList") {
for (const node of mutation.addedNodes) {
if (node.tagName && node.tagName.toLowerCase() === "div") {
console.log(node);
}
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(document.documentElement, config);
};
test();
document.documentElement.innerHTML += '<div>TEST</div>';
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MutationObserver Example</title>
</head>
<body>
</body>
</html>
以下 JavaScript 代码:
document.documentElement.innerHTML += '<div>TEST</div>';
似乎完全重写了
HEAD
和 BODY
节点。
这解释了为什么MutationObserver接口无法拦截<div>
标签的添加。
我想知道如何拦截在这种情况下,向
DOM添加一个
<div>
标签。
突变记录被触发:
function test() {
const config = {
subtree: true,
childList: true
};
const callback = (mutationList) => {
observer.disconnect();
// StackSnippet's console's components have been removed by the innerHTML setter
console.log(mutationList);
document.body.append("Mutation triggered, check your browser's console");
};
const observer = new MutationObserver(callback);
observer.observe(document.documentElement, config);
};
document.querySelector("button").onclick = evt => {
test();
document.documentElement.innerHTML += '<div>TEST</div>';
};
<button>Change <html>'s innerHTML</button>
发生的情况是,对于 HTML 解析器,
<html>
元素只能有 2 个 Element
子元素:一个 <head>
和一个 <body>
。无论您向解析器提供什么输入,它至少都会生成树
#Document
<html>
<head></head>
<body></body>
<html>
如果您在调用后检查生成的树,您会发现
<html>
确实将包含一个空的<head>
,以及一个包含<body>
内容的<div>TEST</div>
:
document.documentElement.innerHTML = `<div>TEST</div>`;
const pre = document.createElement("pre");
pre.textContent = document.documentElement.outerHTML;
document.body.append(pre);
因此,在您捕获的
MutationRecord
中,<div>
列表中没有 addedNodes
,因为实际添加的是 <head>
(文本节点)和 <body>
,其中将包含你的<div>
。如果你想抓住这些,你需要检查addedNodes
的内容。
// StackSnippet's console is disabled by the code below
// So we implement our own "logger".
const log = (content) => {
const pre = document.createElement("pre");
pre.textContent = content;
document.body.append(pre);
};
function test() {
const config = {
subtree: true,
childList: true
};
const callback = (mutationList) => {
observer.disconnect(); // trigger once
for (const { addedNodes } of mutationList) {
for (const node of addedNodes) {
log(`Created node: ${node.nodeName}`);
for (const div of node.querySelectorAll?.("div") || []) {
log(`Added a new DIV: ${div.outerHTML}`);
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(document.documentElement, config);
};
document.querySelector("button").onclick = evt => {
test();
document.documentElement.innerHTML += '<div>TEST</div>';
};
<button>Change <html>'s innerHTML</button>
现在应该注意,有可能到
append()
,将<div>
作为<html>
的直接子代,因此您可能需要添加一行来检查addedNode
.match("div")
,但是如果发生这种情况,肯定是某个地方出了问题。