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:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.1.min.js"><\/script>')</script>
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 Hayden (February 8, 2013 at 11:34 am)
Light wrapper functions are always nice. I usually just define _gaq = _gaq || []; so you can push to it even if there is no library.
wcdolphin (February 8, 2013 at 12:01 pm)
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}}
wanderingbear (February 8, 2013 at 12:22 pm)
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?
Randall (February 8, 2013 at 12:24 pm)
Is it realistic for Ghostery to drop in a __gaq after it blocks GA?
Yahel Carmon (February 8, 2013 at 12:29 pm)
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.
I’ve documented a plugin and a plugin-less workaround for doing this cross-domain tracking unobtrusively here: https://github.com/yahelc/GA-Better-Crossdomain-Tracking
Yvan Boily (February 8, 2013 at 12:42 pm)
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 :)
Hello (February 8, 2013 at 12:57 pm)
Why even use Ghostery?
Jeffrey Nappi (February 8, 2013 at 1:04 pm)
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.
Good to know it actually solves more than one problem :)
Eduardo (February 8, 2013 at 1:04 pm)
The problem would be for sites that span across several domains and have to use cross-domain functionality from Google Analytics to link the visits on all these domains.
In cases like these links to different domains are rewritten as:
External Link
...
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingSite#multipleDomains
Eduardo (February 8, 2013 at 1:06 pm)
Trying again to insert example. Since the last time it was interpreted.
...<a href="http://www.my-example-blogsite.com/intro" onclick="_gaq.push(['_link', 'http://www.my-example-blogsite.com/intro.html']); return false;"> See my blog</a>...<form name="f" method="post" onsubmit="_gaq.push(['_linkByPost', this]);">
Felix (February 8, 2013 at 1:38 pm)
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.
Alexei (February 8, 2013 at 2:27 pm)
Here is a Ghostery support forum thread concerning this issue:
https://getsatisfaction.com/ghostery/topics/ghosterys_blocking_breaks_functionality_on_websites-14gndt
Rob Colburn (February 8, 2013 at 3:40 pm)
Agreed mostly, I like good ol’ use try/catch. Then I’m not relying on more global code.
try {
/* references to 3rd party API */
} catch (e) {}
Jason Osgood (February 8, 2013 at 4:11 pm)
Would it be feasible for Ghostery (or other) to redefine the known tracking APIs? Like a lightweight proxy/stub that does nothing (NOP). Then the client pages wouldn’t break.
Jason Osgood (February 8, 2013 at 4:24 pm)
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.
loris (February 11, 2013 at 4:31 am)
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.
yahoo mail (February 20, 2013 at 2:16 pm)
Yes Ioris, despite the gohstery has been updated in its latest version as you can see in gaq, but john’s contribution is worth appreciation.
way2sms (February 20, 2013 at 2:53 pm)
although you can use ghostery but it would be more better to use third party api with or without ghostery.