Fixing Google Analytics for Ghostery

As an avid user of Ghostery, which blocks all sorts of tracking scripts, pixels, and other web bugs I frequently run across a surprising issue: The case in which the Google Analytics ga.js script has been blocked from loading (which is intended) but then some critical piece of functionality on the site is broken. The most common place I see this is when a site adds some event tracking to a signup or purchase form. Clicking the submit button will result in a JavaScript error, thus making it impossible to submit the form.

I’ve found that this occurs when people are doing inline event tracking and are assuming that the Google Analytics functionality will always exist. Ghostery-or-not this is generally not a good practice to have: Assuming that some external, 3rd-party, script will always correctly load will result in many a broken web page.

You see cases where people work around this issue, for example in the HTML5 Boilerplate project:

This attempts to load the jQuery library directly from Google’s CDN and if it fails (for whatever reason — Google’s down, bad Internet connection, or some other weird reason) then it falls back to attempting to load a local version of jQuery. One question that comes of this is: Why not just always load jQuery from your local source rather than rely upon a third party? In this case it makes a lot of sense to pull jQuery from a CDN as you’ll benefit from the performance and caching benefits that a CDN provides, all while reducing the total bandwidth that you’ll have to expend on your end.

In the case of Google Analytics you typically don’t have that luxury: Google recommends that you load the ga.js library directly from their site (which can make sense as then they can push out seamless API changes and bug fixes). However this still causes issues for when Google Analytics simply doesn’t load, wether due to network conditions or due to Ghostery or some other browser extension.

For this reason, in a site I recently built, I added in the following code as a light wrapper around Google’s event tracking API:

// Add in Google Analytics tracking

var track = function(category, name, value) {

if (window._gaq) {

window._gaq.push(["_trackEvent", category, name, value]);

}

};

and then when I wanted to track an actual event I would do something like this:

track("Source", "Visit", this.href);

If Google Analytics has failed to load for some reason then the track() call just silently does nothing – and for users who have Google Analytics installed it tracks the event. I should note that this same technique applies for other event tracking APIs as well, such as Mixpanel or KISSmetrics (they’re blocked the same as Google Analytics).

Even libraries like Segment.io, which act as a unified wrapper around tracking, are prone to the same issue. Even though they’ve gone through all the hard work of creating a unified API for event tracking they are still blocked by Ghostery, which will likely still result in errors in your application. In that case you would just write a similar track(...) wrapper around their analytics.track('Name', properties) call.

Update: The current version of the Google Analytics script always exposes the _gaq object, even if the code isn’t loaded, so this may be a non-issue for Google Analytics (although the principles here hold true for any third-party-included libraries). I’ve gotten word from the Ghostery team that Ghostery now handles this case, automatically exposing missing global objects for cases like this (see below in the comments). I’ve also gotten word from the Segment.io team that, as of now, Ghostery does not block their analytics tracking so you may not need a wrapper there. Additionally Segment.io does check to make sure that the potentially-blocked global objects are actually there before attempting to use them. In general though, and this goes for all third-party script, you’ll probably want to have a wrapper (or an alternative script) if you’re loading code from a third-party domain name.

Stefan, I have seen a number of people doing something similar, though more explicit, and slightly more verbose. Returning zero, which is consistent with gaq.push in the case of no errors.__gaq = __gaq || {push: function(){return 0}}

While I understand the point, I find the entire trend somewhat disturbing. Presumably users could block any of the numerous scripts a web dev is loading on the page. Are devs expected to do similar workarounds for every single tag that a user could be blocking?

Wrapping in if(window._gaq) isn’t the solution. _gaq actually gets instantiated regardless of if ga.js loads. So, if people are just doing _gaq.push([“_trackEvent”, “foo”, “bar”]); and GA hasn’t loaded, it’ll just push an array onto the array and move on.

The issue is that the popular mechanism for doing cross-domain tracking does a return false onclick. That means that if ga.js hasn’t loaded, return false prevents the click from going through and GA isn’t around to fix it.

Wanderingbear: depends on the objective. If you run a site that uses 3rd party scripts that are used to complete sales, then yes, you should ensure that work arounds are in place so that uses who block content can still complete sales. Of course, if the cost to do that is higher than the value of lost sales (including those lost because of a negative user experience), then don’t do it :)

This is actually precisely how we handle our event tracking. However, our reason was not due to tools like Ghostery, but rather because we would like to avoid tracking specific IPs and have excluded the tracking services from being loaded in those cases.

Hi John, thanks for posting this awesome article! As one of the Ghostery developers, its encouraging to know that Ghostery is in the tool-belt of people like you.

I wanted to add a note here about Ghostery specifics, especially for Google Analytics. Ghostery will actually substitute GA with a dummy surrogate script when blocking is enabled precisely so it doesn’t break when sites use 3rd party stuff to closely. We do this for a number of scripts such as GA, YWA, Omniture, Facebook Connect, and many more. We try to add them when we know that a large number of sites break because of Ghostery.

Nevermind. The link above talks about Ghostery’s “surrogate support”. So answer to my question is “Yes.” and that more support is in the pipeline.

“Surrogate”? Why does every new community have to come up with new names for everything? “Surrogate” is a fine name. But “stub” is already commonly used. Geesh. Reading about “surrogate” for the first time, I’d initially think “Gee, it must be different than a ‘stub’, otherwise they wouldn’t have picked a new name.” And I’d be wrong.

Latest versions of Ghostery are already doing what you are talking about John, look for gaq in their definition file (http://www.ghostery.com/update/all?format=json) and you will see how they substitute the gaq object by a blank one.