The script should be added instead
As discussed in the comments, it is unreliable to remove the script with an observer.
- The browser can prefetch the file as soon as the
src URL is known, independently of the HTML tag being rendered
- The browser does not have to be honest or perfect. It may prefetch the file without listing it (yet) in the developer tools.
- Not all versions of all browsers implement mutation observers: see caniuse MutationObserver. This may attract people using older browsers on purpose and complaining. So not a reliable way for legal requirements.
Instead the script should be added when needed:
(function(p,a,n,t,s){
t=p.createElement(a),s=p.getElementsByTagName(a)[0];
t.async=1;t.src=n;s.parentNode.insertBefore(t,s)
})(document,'script','https://www.googletagmanager.com/gtag/js?id=UA-12345678-9');
Testing your observer locally
The following will illustrate what may be the issue here.
I have created an index.html just as yours but with a locally loaded script test.js. I also added console.log(node); in your observer.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MutationObserver test</title>
<link rel="shortcut icon" href="#">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
const observer = new MutationObserver( (mutations) =>{
mutations.forEach(({addedNodes}) => {
[...addedNodes]
.forEach(node => {
console.log(node);
$(node).remove()
});
});
});
observer.observe(document.head, { childList: true });
</script>
<script src="test.js"></script>
</head>
<body></body>
</html>
test.js
console.log("I got loaded before you could remove me.");
This results in the following console log:
index.html:21 " "
index.html:21 <script src="test.js"></script>
index.html:21 " "
So the observer is working, as it also catches empty nodes with just whitespace characters.
However in the Network tab the test.js is still listed as loaded:
Name Status Type Initiator Size Time
index.html Finished document Other 972 B 1 ms
test.js Finished script index.html 56 B 3 ms
jquery-3... 200 script index.html 31.0 kB 10 ms
index.html Finished text/html Other 972 B 1 ms
It seems as though the HTML node was removed, the browser is prefetching the code. This likely happens because the HTML document itself will transfer regardless. Then the browser parses the HTML and sees all files to be requested. As soon as the HTML document is turned into a DOM the browser runs the observer, removes the node and just in time will not execute the JavaScript from the nodes after the observer.
See this counterexample where I comment out the observing part:
// observer.observe(document.head, { childList: true });
In this case the console output will read:
test.js:1 I got loaded before you could remove me.
The network panel will look nothing different. So it seems as I'm using Chrome, it tries to perfect speed and will load and cache the JavaScript but it will not execute it since its node got removed.
Possible TagManager / Google Anyltics insights
If you have access to alter the TagManager's tags you might just add <script>console.log("TagManager code executed");</script> and confirm this way code execution is correctly suppressed for the removed tag. So confirming the same as my example with a local JS file but in TagManager directly.
Another way is Google TagManager's excellent Debug mode.
If Google Analytics is used here as well you might see your requests (or lack thereof) in the GA Real-Time view (well... bad documentation with no screenshots. Just look for real-time in Analytics you will find it).