Non-Blocking Asynchronous Google Analytics

Adding Google Analytics to your website is an easy way to get detailed visitor stats. All you have to do is sign up for a free Analytics account, and copy-paste a small Javascript snipped on every page of your website (or better still in a common header include file).

Originally, the only problem with this approach was that it added some overhead to your page rendering. That is, if you included the code snipped in your page HEAD section, the page would not continue loading or rendering until the Analytics code was retrieved from Google and executed. People worked around this by putting it just above the closing BODY tag, so it would be the last thing that gets processed in the document and hence would not hold back the rendering of other page elements.

This is a good enough approach for all client-static pages. That is, pages which don't run any client-side Javascript. Otherwise what happens is that whilst the Analytics script is loading and executing, all other Javascript actions, triggers or events on the page get queued up. Typically the Analytics code loads and executes fairly quickly (we're talking milliseconds to one or two seconds max).

Google has recently released a new version of the Analytics snippet code. The new version is loaded asynchronously in the background, which means it should not block page rendering. An issue may still exist however. Consider the case where you are visiting an Ajax heavy intranet site from a corporate LAN, where your browser has no external internet access. Let's presume the Analytics asynchronous snippet is placed just above the closing BODY tag. The page loads fully in a split second from the intranet, and then attempts to load the Analytics code. Since you have no internet access however, this operation will timeout in about 10 to 15 seconds.

Depending on how your website is coded, and especially if you use lots of document.ready function wrappers for your Ajax manipulations, it is entirely possible your website will not execute any DOM updates until the Google code timesout. Now if a user clicks on something and expects a result within a split second, 15 seconds is a very, very long time to wait.

I'm not sure what the best solution to this case is. The workaround I've come up with is to only attempt to load the Google Analytics code if the client has internet access to Google servers. That is, attempt to first load an invisible image hosted by Google, and trigger the Analytics loader only once the image is loaded. If the image never loads, don't bother trying to load Analytics, and hence avoid the long 15 second blocking timeout window. The code to do this is shown below:


<div id="google-analytics-stuff" style="display:none">
<img src="http://www.google.com/images/logos/analytics_logo.gif#<?=time()?>" alt="" style="display:none"/>
</div>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-111111-1']);
_gaq.push(['_trackPageview']);

var loadAnalytics = function() {
var 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';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
}

$("#google-analytics-stuff img").load(loadAnalytics);
if($("#google-analytics-stuff img")[0].complete) loadAnalytics();
</script>


Just remember to replace the UA-11111111-1 with your own Analytics serial. Also, note the <?=time()?> in the image src URL. This is a PHP function that prints out the current timestamp as number of seconds since epoch. We add this to the end of the URL such that the image is never loaded from cache (we're trying to establish connectivity to Google). The rest of the code is fairly standard, although I've used jQuery for the last two lines to bind the Analytics loader function to when the image completes loading.

Comments

  1. Instead of using window.onload, use DOMContentLoaded or something like jQuery's .ready() method to detect when the document is ready. This will ensure that async scripts like GA don't block your "Ajax manipulations".

    Demonstration of how to do this:
    http://analytics.nfshost.com/ready.html

    jQuery ready method API:
    http://api.jquery.com/ready/

    ReplyDelete
  2. I understand the usefulness of the $().ready() function and use it extensively in one of my applications to wrap all Ajax calls in it (this prevents all sorts of weird nasties in IE6 including some race conditions).

    If I include the Analytics code and wrap in in a .ready() function, but the code times out, then for some reason other Ajax calls which communicate with the backend and then display a popup dialog or refresh a DIV get queued behind the Analytics function.

    It's entirely possible I may be doing something wrong... the hack I posted above seems...dirty.

    If anyone experiences the same problem I described and can put together a simple page to replicate, that would be great.

    ReplyDelete
  3. I think I've worked it out now. After triggering the Asynchronous loading of the Analytics SCRIPT tag, you cannot add any more SCRIPT tags to the document dynamically until the Analytics tag completes loading, or timesout trying.

    So if you begin loading Analytics, and you then run an Ajax method that retrieves some Javascript content from the backend, and then you try to insert this new code into the DOM in a new SCRIPT tag, the operation will get queued.

    I've created a simple testcase to demostrate this: http://dl.dropbox.com/u/642364/blogger/scripts/analytics.html

    ReplyDelete

Post a Comment

Popular posts from this blog

Wkhtmltopdf font and sizing issues

Import Google Contacts to Nokia PC Suite

Can't delete last blank page from Word