documentElement节点使用innerHTML时不会触发MutationObserver

问题描述 投票:0回答:1

我发现了 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>标签。

javascript dom dom-events innerhtml mutation-observers
1个回答
0
投票

突变记录被触发:

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 &lt;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 &lt;html>'s innerHTML</button>

现在应该注意,有可能

append()
,将
<div>
作为
<html>
的直接子代,因此您可能需要添加一行来检查
addedNode
.match("div")
,但是如果发生这种情况,肯定是某个地方出了问题。

© www.soinside.com 2019 - 2024. All rights reserved.