IMPORTANT: AdBlock no longer blocks the dc.js Google Analytics Tracking Code.

As excited as our team were to get smart with Google Analytics Remarketing, we were hesitant to roll out the new dc.js tracking code as we knew it would be blocked by AdBlock and other similar ad-blocking browser extensions.

But how many people are actually using AdBlock, and why do we care about them? We’re not going to be able to remarket to them anyway!

This article details our experiments with measuring AdBlock users, and our solution. Feel free to ignore the detail and skip straight to the code!

Why do we care about AdBlock users?

The reason we care is that in enabling the new remarketing features by switching to the dc.js tracking code, we lose all tracking for those visitors who block it. This means that our visitor numbers will be down, and a specific, likely tech-savvy section of our user base will be excluded from our analytics. Less than ideal!

It was very easy to verify that the dc.js tracking code was blocked by installing AdBlock in Chrome and checking to see if dc.js loaded. It didn’t!

double-click-remarketing-01-2

It’s worth noting that there are a large number of users who won’t be able to be targeted with remarketing. This feature is dependent on third-party cookies — by default, Safari has always blocked third party cookies, and Firefox has recently announced that they will be setting a similar default starting with version 22, which is due for release around June 2013. That’s around 50% of visitors for many sites. We’ll have to just accept that we can’t remarket to these visitors, and can’t show ads to people with AdBlock. But there’s no reason we can’t track how they engage with our site, and use that data to drive improvement.

How many people are using AdBlock?

AdBlock is the single most popular extension for Firefox, Chrome and Safari. On Firefox alone there are more than 15 million users. That seems like a lot, but there are just under 7 billion people in the world, of which, around 30%, or roughly 2 billion use the internet. Even 50 million people is a drop in the ocean when we’re talking about 2 billion internet users. The problem is, that a lot of them are the users that we care about — wealthier, more educated, tech-savvy users who spend money!

double-click-remarketing-02-2

The experiment: measuring AdBlock visitors

For the Loves Data website, we initially wanted to find out if the proportion of visitors who were blocking dc.js was significant enough that we cared to track them. Without implementing the dc.js tracking code, we created a script that loads the dc.js script into a text variable, which logs an event in Google Analytics on success or failure.

Our results showed that around 6% of visitors to our website were blocking the new dc.js script, while 94% of them were loading it successfully. Too many visitors to ignore!

double-click-remarketing-03-2

The solution: a simple ga.js fallback

We initially thought that ga.js and dc.js could run side-by-side, with ga.js as the main tracker, and dc.js tracking into a separate profile to be used exclusively for creating remarketing segments. A quick test, and a comparison on the ga.js and dc.js scripts showed that the scripts are almost identical, and they create the same global variables in JavaScript.

In lay terms, this means that if we load one script and then the other, the second script will clobber the first script. In really simple terms…we can’t do it!

So we had to find a way to check if dc.js had loaded and if it hadn’t, load ga.js as a fallback. There is an existing solution that attempts to load a self-hosted JavaScript called ‘advertising.js’. If the script loads successfully, it is assumed that AdBlock is not installed and dc.js is loaded. If it doesn’t load, it is assumed that AdBlock is installed, and ga.js is loaded.

We weren’t happy with this approach for a number of reasons:

  • As a digital marketing company with many clients, we wanted a solution that involved minimal code changes; it would take a long time to get our clients to upload ‘advertising.js’.
  • We were not happy with the assumption that a test for a self-hosted ‘advertising.js’ was a satisfactory check for dc.js, which is hosted at stats.g.doubleclick.net.
  • And finally, we wanted a solution that could be rolled out entirely within Google Tag Manager.

So what we needed was a single script that could run in-line or be loaded in with GTM. The script would need to:

  1. Attempt to load dc.js from stats.g.doubleclick.net
  2. Check if it loaded successfully
  3. If it didn’t load successfully, load ga.js from google-analytics.com instead

The first step is easy. We run the standard dc.js tracking code as detailed on the Google Analytics support site.

 var _gaq = _gaq || [];
 _gaq.push( ['_setAccount', 'UA-XXXXXXX-1'],['_trackPageview'] );
 (function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
 })();

The next step is not as straightforward. We need to give the script sufficient time to load, check if it loaded succesfully, then execute our logic.

There are two events that we can try listening for: document.onload and window.onload. To know which to use, we really have to understand the difference between them and the way that an HTML page is loaded into the browser.

The best overview of the client-side JavaScript timeline that I’ve come across is on pages 323-324 of the 6th edition of JavaScript: The Definitive Guide. We need to wait until the point where we can reliably assume that all asynchronously loaded scripts have been executed.

Unfortunately, these are the last things to run, and we can only assume these scripts have loaded after all content has been loaded.

document.onload fires at the transition from synchronous script execution to asynchronous script execution. That is, when the entire HTML document has been loaded, and synchronous scripts have been executed, but asynchronous scripts have not necessarily executed.

window.onload fires when the page has completely loaded, all scripts have been executed, and all images downloaded.

This is a bit later than we’d like, but it’s the only reliable event that we can listen for. This does mean that there will be a slight drop in tracking accuracy, but that is only for the 6% of users who are blocking dc.js. For us, this is an acceptable compromise.

So the next step is to attach a function to window.onload that will check if our tracking object has been instantiated. We have already created the _gaq tracking object, so we have to check if some internal variable has been defined.

 window.onload = function() {
  if(_gaq.I==undefined){
   alert('dc.js did not load successfully');
  } else {
   alert('dc.js loaded successfully');
  }
 };

Here we have a working test that will show an alert message to indicate whether dc.js loaded successfully or not.

The only remaining step is to replace our unsuccessful alert with code to load ga.js, and if we like, we can replace our successful alert with an event to log the fact that dc.js was successfully loaded. We actually track events for both successful and unsuccessful loads, so we can get an idea of what proportion of visitors are using each script.

If you are using events at this point, be sure to set the non-interaction flag to true. If you don’t do this, every visit will be registered as having interaction, and your bounce rate will drop to zero!

 window.onload = function() {
  if(_gaq.I==undefined){
   _gaq.push(['_trackEvent', 'tracking_script', 'loaded', 'ga.js', ,true]);
   ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
   ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
   s = document.getElementsByTagName('script')[0];
   gaScript = s.parentNode.insertBefore(ga, s);
  } else {
   _gaq.push(['_trackEvent', 'tracking_script', 'loaded', 'dc.js', ,true]);
  }
 };

Combined with the original dc.js snippet above, here’s the complete customised snippet. Replace UA-XXXXXXX-1 with your own UA number, and it’s good to go. It should be placed just before the closing tag, replacing your existing Google Analytics snippet or included as a custom HTML tag in Google Tag Manager.

 

 var _gaq = _gaq || [];
 _gaq.push( ['_setAccount', 'UA-XXXXXXX-1'],['_trackPageview'] );

 (function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
 })();

 window.onload = function() {
  if(_gaq.I==undefined){
   _gaq.push(['_trackEvent', 'tracking_script', 'loaded', 'ga.js', ,true]);
   ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
   ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
   s = document.getElementsByTagName('script')[0];
   gaScript = s.parentNode.insertBefore(ga, s);
  } else {
   _gaq.push(['_trackEvent', 'tracking_script', 'loaded', 'dc.js', ,true]);
  }
 };