I've discovered the MutationObserver interface for intercepting and analyzing changes in the DOM.
It works very well and I've managed to intercept all DOM changes... with the exception of the DOM change via the innerHTML method when it's the documentElement node that uses it.
Here is my code:
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>
The following JavaScript code:
document.documentElement.innerHTML += '<div>TEST</div>';
appears to completely rewrite the HEAD and BODY nodes.
This explains why the MutationObserver interface fails to intercept the addition of a <div> tag.
I'd like to know how to intercept in this case, the addition of a <div> tag to the DOM.
The mutation record is triggered:
What happens is that for the HTML parser, the
<html>element can only have 2Elementchildren: a<head>and a<body>. No matter what input you give to the parser, it will produce at least the treeIf you check the produced tree after your call, you'll see that indeed the
<html>will contain an empty<head>, and a<body>which contains the<div>TEST</div>content:So in the
MutationRecordyou catch, there is no<div>in the list ofaddedNodesbecause the ones that have actually be added are the<head>, a text node, and the<body>, which will contain your<div>. If you want to catch these, you need to check the content of theaddedNodes.Now it should be noted that it is possible to
append()a<div>as a direct child of the<html>, so you may want to add a line that checks that theaddedNode.matches?.("div"), but if this happens, something is certainly messed up somewhere.