Reputation: 1343
Disclaimer: I am aware of why should I avoid using document.write and document.write clears page.
Let's say I need to inject a synchronous (blocking) script into the DOM when it's being loaded (document.readyState = 'loading'
), but right before another specific existing sync script.
If I have access to HTML code, I can do this by writing something like:
<html>
<head>
<title>It's okay</title>
<script>
document.write(
'<script src="https://www.googletagmanager.com/gtm.js?id=GTM-WZNT8QT" onload="console.log(1)"><'+'/script>'
);
</script>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" onload="console.log(2)"></script>
</head>
<body>
<script>console.log('DOM is parsed')</script>
</body>
</html>
It correctly loads both JS files, and outputs 1
, 2
, DOM is parsed
to the console.
Now, if I try to do it using MutationObserver
(that is, detecting when a new script node is inserted, but before the script is actually invoked),
<html>
<head>
<title>Why not loading?</title>
<script>
// Say, this JS is invoked by the extension before even parsing HTML
new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
for (const node of mutation.addedNodes) {
if (node.tagName !== 'SCRIPT' || !node.src.includes('jquery')) {
continue;
}
document.write(
'<script src="https://www.googletagmanager.com/gtm.js?id=GTM-WZNT8QT" onload="console.log(1)"><'+'/script>'
);
}
}
}).observe(document, { childList: true, subtree: true });
</script>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" onload="console.log(2)"></script>
</head>
<body>
<script>console.log('Now DOM is parsed!')</script>
</body>
</html>
...not only it makes the browser "always loading" something, but also never prints 2
and Now DOM is parsed!
to the console. Upon checking the actual HTML code, there's only that "googletagmanager" script in the head. JavaScript thread is not blocked. And actually document.readyState
always stays 'loading'
from that moment, effectively stuck and broken.
Questions:
With MutationObserver
actually, I can at least prevent the jquery
code from running by saying node.setAttribute('src', 'text/prevented')
in the mutation handler, so it doesn't matter whether googletagmanager script is inserted before or after it. I am trying to understand why it doesn't work.
Upvotes: 1
Views: 129