Skip to content

Instantly share code, notes, and snippets.

@elbywan

elbywan/blog.md Secret

Last active June 16, 2018 22:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elbywan/daf5ba424f9199abd8ec317d7594e94c to your computer and use it in GitHub Desktop.
Save elbywan/daf5ba424f9199abd8ec317d7594e94c to your computer and use it in GitHub Desktop.

Please look there for the final article


GDPR Compliant Website Analytics — Blocking analytics scripts until consent

The problem

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 😉.

Preventing a <script> tag from executing

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.

Using MutationObserver to observe the script tag insertion

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.

Changing the script type attribute

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.

Using the beforescriptexecute listener

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

Using the beforeload listener

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

Last resort for Edge - mark script tags with a custom type manually

Nothing above will work with Edge, so as a last resort we have to mark it manually:

https://gist.github.com/2441874ed4b3dcfc983678130d291b95

Preventing the execution of dynamically inserted script tags

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.

Monkey patching document.createElement

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

Overriding 'src' and 'type' descriptors on the HTMLScriptElement prototype

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

Re-enabling these scripts afterwards

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>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment