I want to stop the execution of one single line from a site, so that the whole page is read by the browser except that single line. Or the browser may simply skip the execut
beforescriptexecute no longer works in Firefox, nor did it work in Chrome. Luckily, there's an alternative, using MutationObserver, which is quite widely supported. The general idea is to add a MutationObserver at the beginning of pageload, which will run a callback whenever a new node is added to the DOM. Inside the callback, check for the existence of the tag you want to alter or remove. If it exists, you can tamper with it (such as change its textContent, or have its src point somewhere else). Only after the callback finishes will the newly added script tag run, so this is an effective way to intercept and change Javascript on a page. Here's a live snippet example:
Example content
Here's an example userscript that blocks jQuery from loading in the here on Stack Overflow:
// ==UserScript==
// @name Example jQuery removal
// @include https://stackoverflow.com*
// @run-at document-start
// @grant none
// ==/UserScript==
if (document.head) {
throw new Error('Head already exists - make sure to enable instant script injection');
}
new MutationObserver((_, observer) => {
const jqueryScriptTag = document.querySelector('script[src*="jquery"]');
if (jqueryScriptTag) {
jqueryScriptTag.remove();
observer.disconnect();
}
})
.observe(document.documentElement, { childList: true, subtree: true });
If you install this, you'll see that the loading of jQuery fails, resulting in lots of errors generated by Stack Overflow's JS.
Make sure to attach the MutationObserver as soon as possible - you need @run-at document-start to attach it before the page loads anything inside the . (If using Tampermonkey / Chrome, you may need to enable experimental instant script injection to achieve this reliably - go to Tampermonkey Settings, Config mode: Advanced, scroll to the bottom, set Experimental Inject Mode to Instant.)
If you're writing userscripts for others, any you're using this technique, make sure to include instructions for instant script injection, since injection is not instant by default on Chrome.
Note that the observer is attached using
.observe(document.documentElement, { childList: true, subtree: true });
This attaches the observer to the element, watches for added and removed immediate children with childList: true, and watches for added and removed nodes anywhere inside its descendants with subtree: true. Such a recursive listener is useful, but it's also computationally expensive on large dynamic pages, so make sure to remove it once it's achieved its purpose.
On a huge page, calling querySelector on every mutation could be costly, so you might want to iterate through the mutations (the first parameter to the observer's callback) and the mutations' addedNodes instead:
Example content
You can also tweak inline scripts by assigning to their textContent inside the observer callback. The following snippet shows how you can change a random number generator function to always return 10, rather than 1-6:
Results:
Method 1 is the technique you can use when writing userscripts for others. But if the userscript is just for yourself, there's an easier method that can work in some situations. See this answer to the question Chrome Dev Tools - Modify javascript and reload: by going to the Sources -> Overrides tab in Chrome Devtools, and enabling local overrides, you can tell the browser to load a local copy of a .js instead of downloading the site's version. Then you can edit the local copy as desired, and it'll run instead of the built-in script. This is especially useful when tweaking large Javascript files - it's much more manageable than the MutationObserver approach.
But, there are some downsides:
tags, you'll have to download a local copy of the page. (So if the page serves dynamic content through the HTML response, you'll either have to re-save and re-tweak the .html for your overrides to use the new content, or you'll have to go back to the MutationObserver method.)