Making google analytics unobtrusive – on 20th July, 2008

Something as simple as page tracking shouldn’t happen in an obtrusive single point of failure way. Here’s a little script to get around that.

Whenever I implement a new tracking script it always strikes me how obtrusive the scripts tend to be.

They tend to put the script either at the start or the end of the document body, and they tend to use nasty document.writes. I love google analytics but sadly on this count it is no different. In this article I look at implementing google analytics in a less obtrusive way.

You may wonder why I would want to look at the implementation of something which does wholly what it sets out to do, so I will start by explaining just a little about how I think the implementation should work.

  1. In order to create a manageable code base I want to make sure the javascript is located in just one place. That is to say, if I want to modify the code at some point, I want to do it in just one place
  2. In order to re-use the code I want it to be easily transferrable, without heavy code re-writes
  3. To maintain the integrity of my website I want the behaviour (javascript) to be seperated from the content (html), just in the same way that we do with layout (css) and content (html)
  4. Should heaven forbid, google’s servers fail to respond quickly I don’t want it to delay the loading of the whole webpage. This could be a point of contention for some people as if the webpage loads without the analytics script, and a person navigates away to another page, then we have not tracked them. Not good. I would question those worried about this scenario as follows: Which is more important in helping the person fulfill their objectives on your website, the tracking script, or the ability to use your pages as they are meant to be used without waiting. I think the answer is clear.NOTE: Your tracking script provider might have some issue with this. I have heard of in some cases the preference for the tracking script to be after the opening body tag. I think however that you need to think about the people who use your website rather than the ability to track their movements. Having the script in line might also help providers maintain a 99.9% uptime record, since if their server is down their script will stop your page loading. This in turn will make you less likely to navigate to a new page, which will mean that in theory at least, the server has been down less.

So how do we achieve this

Simple really, using DomScripting methods we will

  • seperate the javascript into it’s own file (This fulfills 1,2 and 3)
  • call the javascript on page load instead of in line (This fulfills point 4)

Step 1 – grab your starting code

In my case I was provided with the following code by Google Analytics

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-999999-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>

There are two parts to this script. The first actually loads an external .js file from google using the nasty document.write. The reason it loads it in using javascript is that the script is determining whether it resides on a secure server or not. This resolves any issues with ‘mixed’ security error messages on secure servers.

The second part assigns your unique user id and calls a couple of functions from the tracking code

Step 2 – putting it all in one script

So the first thing we need to do is create a script and insert it into the head of your document. (Or wherever you are importing the rest of your scripts)

<script type="text/javascript" src="/wordpress/wp-content/themes/default/js/googleAnalytics.js"></script>

Step 3 – heres the codey bit

The first thing we need in our code is a function that will call the Google Analytics function on the DOM event of page load. There are lots of references out on the web detailing scripts which allow multiple onLoad functions to be called, and I believe that the one used here is based on Simon Willisons addLoadEvent script. This script is a function that that you can call, to in turn call other functions on page load:

/* Simplifies onload, you will no longer have to add an onload event call just call addLoadEvent */
function addLoadEvent(func,arg){

if (!arg){
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
}
}
  else{/*if the onload event has an argument/parameter cater for that*/
  if (arg){
  oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func(arg);
    }
     window.onload = function() {
      oldonload();
      func(arg);
    }
  }
}
}

Now we have a function which can call our analytics function on load we just need to convert the javascript to make it a littel less obtrusive. The more tricly bit involves loading the external js file which I mentioned earlier in part one. For this we create a function which appends the script to the head of the document. This script is called immediately

function loadGAScript(){
    /*Check browser for Dom compatibility*/
if (!document.getElementsByTagName) return false;
/*Determines whether the page is using a secure or unsecure protocol*/
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
/*Writes in the script to the document head*/
var gaScript = document.createElement("script");
gaScript.setAttribute("src",gaJsHost +"google-analytics.com/ga.js");
gaScript.setAttribute("type","text/javascript");
var domHead = document.getElementsByTagName("head")[0]
domHead.appendChild(gaScript);
    }
    loadGAScript();

We then just need to call ‘part two’ of the script on page load. SO we create a function out of it:

/*Calls the analytics function*/
function callGA(){   
var pageTracker = _gat._getTracker("UA-999999-1");
pageTracker._initData();
pageTracker._trackPageview();
}

and then once the dom has loaded we will call this function

addLoadEvent(callGA);

Putting it all together

So heres the easy bit.

  • Download the javascript file
  • Add a reference to the script in your document
  • Customise your user id

and you’re done

  1. Jason Runk says:

    Good idea, but there is still a fundamental problem. Now that you have located everything into a local file, the .js header still needs to be included on every page. In our current application, we have a J2EE application with over 500+ jsps that need to be pre-compiled before running in the EAR file.

    Opposed to actually touching each of the files, I wonder if we could have Apache append the .js required for Google Analytics to each of the requests?

  2. A Reader says:

    Great tutorial! Thanks a lot for the scripts!

  3. Peter Bowey says:

    Great to see that there are some foresighted people that understand how to insert javascript unobtrusively. In my years of dealing with HTML, CSS, PHP, Javascript, and now XHTML – I am amazed at how many current website designers still drop so much Javascript direct into (X)HTML.

    Your guide is a great seed for those that will soon change their coding methods.

    Thanks so much for sharing this template and skill! I have tested it and it works fine!

    Peter Bowey
    Computer Engineer

    Peter Bowey Computer Solutions
    69 Sutherland Ave, Hayborough,
    Victor Harbor, SA, Australia, 5211
    EMAIL: support@pbcomp.com.au
    WebSite: http://www.pbcomp.com.au
    ABN: 22 145 153 678

    Your Partner in Today’s Computer Technology!

  4. Jason – you can have Apache append the js call/file (or append any other text or code) to delivered pages by using the mod_layout module (http://tangent.org/362/mod_layout.html). I use this regularly and describe it in the book Advanced Web Metrics with Google Analytics.

    HTH, Brian

  5. Tom says:

    Awesome work, Al. Subscribed to your RSS.

    Keep up the good work!
    Tom

  6. Chris says:

    Cracking idea and no obvious errors on my site, but on GA it shows tracking status up as unknown.

    Tracking Not Installed (Last checked: Feb 13, 2009 2:40:47 AM )
    The Google Analytics tracking code has not been detected on your website’s home page

    Given that all I’ve done is take your code and add my ID, any possible reasons why it’s not functioning?

    Any pointers appreciated

    Chris

  7. Chris says:

    *Update* For some reason my site doesn’t like it being in a subfolder. No idea why, but it works, so I wont fiddle!

    Cheers again.

  8. Wayne says:

    Jason brings up a good point, but I think there’s no reason that this code can’t be added to an existing Javascript library which your 500+ pages already reference. That’s why you can build JS libraries in the first place, you can incorporate many functions into them. You just have to have some advanced DOM scripting functions to make it work… like, not coincidentally, Simon Willison’s addLoadEvent. Al is putting it into a separate file, but I think there’s no reason it shouldn’t work integrated either.

  9. Al Stevens says:

    @Wayne I absolutely agree. My ideal solution would be to add it to a DOMScripts library. From a tutorial point of view I figured it would be easier to separate it out into a standalone script.

  10. Excellent article Al.

    I like your thinking very much, this kind of thing with inline scripts annoys me immensely too.
    I have to have the cleanest source possible and I like everything logically sorted into separate files.

    Almost a year on from your original post, jQuery is now thankfully very mainstream.
    So I’ve compacted your example a little further by using jQuery’s built-in document.ready function.
    This way if your site is already using jQuery (most sites are thesedays), then you don’t need to bring along your own addLoadEvent function.

    So I’ve ended up with the following in my /js/googleAnalytics.js file:

    function loadGAScript() {
    if (!document.getElementsByTagName) return false;
    var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);

    var gaScript = document.createElement(“script”);
    gaScript.setAttribute(“src”, gaJsHost + “google-analytics.com/ga.js”);
    gaScript.setAttribute(“type”, “text/javascript”);
    var domHead = document.getElementsByTagName(“head”)[0];
    domHead.appendChild(gaScript);
    }

    function callGA() {
    var pageTracker = _gat._getTracker(“UA-999999-1″);
    pageTracker._initData();
    pageTracker._trackPageview();
    }

    $(document).ready(function() {
    loadGAScript();
    callGA();
    });

    Super clean and love it! :) I’ll do this on all my sites now, many thanks.

  11. Exactly what I was searching the web for!

    I am trying to rid my site from having javascript dropped in the middle of my html and you have helped with that.
    Keep up the great work!

  12. Thanks for the cool idea,

    since I am using mootools, I was able to further simplify the script. In my standard Javascript library that I use in most projects, I have the lines:

    if ($$(“meta[name=gatracker]“).length>0)
    {
    var gaJsHost=((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
    new Asset.javascript(gaJsHost+”google-analytics.com/ga.js”,{ ‘onload’: function(e)
    {
    var pageTracker = _gat._getTracker($$(“meta[name=gatracker]“)[0].content);
    pageTracker._initData();
    pageTracker._trackPageview();
    } });
    }

    Now to activate Google Analytics in any of my websites, I just have to put

    inside the document head. This way I can have the same javascript work with different tracking IDs/customers.

    Regards,
    Michael

  13. Sorry, html tags seem to be stripped, the code to add to the document head is:

    meta name=”gatracker” content=”UA-999999-1″

    Regards,
    Michael

  14. Paul Solomon says:

    Very nice approach; thanks so much!

    The code I recently was given by Google is a bit different, in that the second script uses the try/catch(err) functions:

    try {
    var pageTracker = _gat._getTracker(“UA-999999-1″);
    pageTracker._trackPageview();
    } catch(err) {}

    Can you recommend the best way to modify your script to accommodate this? Would it just be:

    function callGA(){
    try {
    var pageTracker = _gat._getTracker(“UA-999999-1″);
    pageTracker._trackPageview();
    } catch(err) {}
    }

    Thanks!

  15. Jeremy says:

    Hi,
    I implemented the JQuery approach above. I checked the code runs without errors by placing javascript alerts at the ends of each function call. It seems to be ok. But, my google analytics page is saying that ‘tracking is not yet installed’.

    I’ve submitted a sitemap and added my site via Google’s add-a-url page…

    Any ideas what could be wrong??

    Many thanks for your time
    Jeremy

  16. Al Stevens says:

    Paul – Yes, I think Google have updated their standard tracking code. All the more reason to store the code centrally! I can’t see any reason why that wouldn’t work just fine, although I haven’t had a chance to test it.

    Jeremy – Your issue could be linked to the updated version of the tracking code. I know I had a client with mixed versions on their website and the older version wasn’t registering properly. I’ll put some time aside to posting the updated version of the code using Aaron’s Jquery modifications.

  17. Francisco says:

    Hello there. I just wanted to say thanks for this great and useful technique for script embedding, and also for raising awareness about these things. I think most people that do not know much about coding or programming (I include myself in this category) tend to trust certain plugins or scripts just because “they come from the big guys,” and are usually unaware of the disadvantages and downsides involved.
    BTW, I posted a little summary and link on the WordPress Forums (http://wordpress.org/support/topic/359297?replies=9#post-1390382).
    Keep up the good work! Thanks again.

  18. Jackie says:

    Al,
    I am a novice at this so pardon my stupid question. I have a WordPress blog using the Piano Theme 1.3 and would like to put Google Analytics unobtrusively on the index.php but I don’t see or there. I post to my blog twice a week under 9 different categories so I figure it is simpler to track only the index page. Where would I put the code, and since I don’t see an external js file, how/where do I put the code for the function call?

  19. Jackie says:

    Missing info from my post: Index.php doesn’t have body or head tags.

  20. Austin says:

    This is such a great article, and all the commenters are so helpful. Would still really love to see the Jquery example used with the new updated analytics code from google!

  21. Al Stevens says:

    Hi Jackie,

    You should really be editing your header include. That contains the tags where you can insert the google analytics script. If your file doesn’t have a body tag then it won’t be valid xhtml. Try going to Theme editor and looking for header.php

  22. bobdobbs says:

    Hey Aaron.
    Thanks for the tecnique.

    However, when I put it into place, firebugs console gives me errors.
    I suspect that your snippet might not work with the latest version of analytics.

  23. K. M. says:

    This is G-R-E-A-T great! I just could not hack the script right to make it work in an external .js file, and asking around on Google forums was not helpful.

  24. Levi says:

    I was wondering if this would work:

    function loadGAScript()
    {
    if (!document.getElementsByTagName) return false;
    var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
    var gaScript = document.createElement(“script”);
    gaScript.setAttribute(“type”, “text/javascript”);
    gaScript.setAttribute(“src”, gaJsHost + “google-analytics.com/ga.js”);
    var domHead = document.getElementsByTagName(“head”)[0];
    domHead.appendChild(gaScript);
    }
    loadGAScript();

    function callGA()
    {
    try
    {
    var pageTracker = _gat._getTracker(“UA-xxxxxxxx-x”);
    pageTracker._trackPageview();
    }
    catch(err) {}
    }

    function doThisAfterTheHTMLHasLoaded()
    {
    callGA();
    }

    if(typeof window.addEventListener != “undefined”)
    {
    window.addEventListener(“load”, doThisAfterTheHTMLHasLoaded, false);
    }
    else if(typeof window.attachEvent != “undefined”)
    {
    window.attachEvent(“onload”, doThisAfterTheHTMLHasLoaded);
    }

  25. Levi says:

    I forgot to say thankyou for this tutorial. Thankyou. I like it

  26. Levi says:

    I tried the code I wrote above and it works in chrome, not sure what version, firefox, not sure what version, and ie6. I still need to test it in ie7, ie8, and safari. My original question should have been: I’m new to javascript, how does the code I posted compare to your code? I usually use the simplest code that will work in the most used browsers. The code I posted I got out of a book. To me it seems simpler and easier to understand. What do you think?

  27. Levi says:

    I meant to say, this is the code I got out of a book.
    The other code is from this tutorial.

    if(typeof window.addEventListener != “undefined”)
    {
    window.addEventListener(”load”, doThisAfterTheHTMLHasLoaded, false);
    }
    else if(typeof window.attachEvent != “undefined”)
    {
    window.attachEvent(”onload”, doThisAfterTheHTMLHasLoaded);
    }

  28. Al Stevens says:

    @Levi – thanks for your input. I don’t see any fundamental problem with the script you have used and I think the answer to your question lies in whether you will be calling multiple functions on page load. You see if you have other functions to call on page load (like for example a script which loads another web service) then using a reusable function like addLoadEvent makes sense. If however you only require a single use then maybe your code will suffice.

    Alternatively if you’re using a script library like jQuery then you could simply use the library’s own function, in jQuery’s case – $(document).ready(function() {
    // put all your jQuery goodness in here.
    });

    I hope that helps.

    Al

  29. Al Stevens says:

    @bobdobbs I’m hoping to upgrade this tutorial soon! I hope you managed to solve it though.

  30. Levi says:

    Al, yes that helps, thanks for your response.

  31. [...] » Making google analytics unobtrusive Al Stevens – Art director, interactive and user experience … (tags: private googleanalytics) [...]