Note: This gist a only a draft related to this blog post: https://medium.com/snips-ai/how-to-block-third-party-scripts-with-a-few-lines-of-javascript-f0b08b9c4c0
Here at Snips we take privacy very seriously. Now that the GDPR is in effect, we have decided that our websites should not collect analytics on people visiting - unless they opt-in voluntarily.
Blocking third-party analytics until a user provides opt-in can be a bit tricky. The problem is that these analytics services are built to start immediately, and that they rely on small minified code snippets and script tags for this. You can try modify the minified code yourself, but it get's a bit messy and hard to maintain, especially if you use several libraries.
So in order to handle this better - automatically - we created a small open source library called yett that takes care of blocking the execution of analytics scripts. The library also allows you to later unblock these scripts, once a user has opted in.
To block scripts automatically might seem trivial - but it's actually not that easy to block inlined script tags!
So while digging into the subject, we found some technical subtleties that we thought would be nice to share with the community 😉.
This was completely unknown territory, since it is quite unusual to blacklist scripts that you inserted yourself.
Here are the requirements: the controlling script must be loaded synchronously, before any other script. At the moment this script loads, you have clue what the final html will look, since the document has not been fully loaded yet.
Here are a few techniques that will actually work.
MutationObserver comes in handy in this case, since you can just register this observer on the document element and be notified whenever nodes get inserted.
https://gist.github.com/2967eb39e10ad219e95b166bb77fa2df
But this alone won't be sufficient of course, but at least it will be a first step towards our goal.
This does the magic in Chrome
, Safari
and IE
.
Changing the type
attribute to something else than application/javascript
before the script request is resolved actually allows us to completely stop the script from executing!
https://gist.github.com/2d73c7e27d0df8dfe193dbd440cb845c
Unfortunately, this won't work for Edge
and Firefox
.
The beforescriptexecute event is marked as deprecated, but is still supported by Firefox since version 4.
Unfortunately, event if this is considered as bad practice we don't have much choice other than to call it.
The good news is that is works perfectly 😄.
https://gist.github.com/5138c9f3bbafa3f5826186e022beadae
On top of that as a bonus, Safari
has a nice event that will event prevent a script from loading!
https://gist.github.com/5f554606e4bfaff5d7efd59f77d86cc0
Nothing above will work with Edge
, so as a last resort we have to mark it manually:
https://gist.github.com/2441874ed4b3dcfc983678130d291b95
Scripts can also be created and appended to the DOM programmatically as you should know. The mutation observer will catch those, but changing the type or the events won't work on these ones.
So, if we cannot prevent the execution after these scripts have been inserted, we still can return a slightly modified script instance when the script element is created.
https://gist.github.com/d3c853259940fd0ac8a9315f0c360389
The last step is to ensure that the type is set to javascript/blocked
when the third-party code changes the source of the HTMLScriptElement to something that we want to block.
https://gist.github.com/1aaef6abd33967b2b260c809bcc50384
Of course, after blocking these scripts you may want to run them afterwards.
The process is simple, just backup every script node the mutationObserver catches with type javascript/blocked
inside an array and expose a function which puts back those nodes into the <head>
.