Thursday, 17 March 2011

How to automatically track events with Google Analytics async snippet and jQuery

This article examines a way to use a feature of Google Analytics called Event Tracking. By using jQuery and the Google Analytics JavaScript API we will automatically track events on your site not seen by a standard analytics installation.

Updated article - async snippet version

This article was re-written for what is currently the latest tracking code style, known as the async snippet. If your code looks like the snippet below then this article is for you. If you're still using the old pageTracker code and you're not in a position to upgrade at the moment then you can read the old version of this article here.

<script type="text/javascript">

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

  (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);
  })();

</script>

Don't forget that this snippet now goes right before the </head> tag instead of the </body> tag that it used to go in.

I'm going to leave the rest of this article as is, except for tweaking the facts. Its good to see that this article actually only needed one line changing in its code to support the new format. You will read about how I refactored the jQuery code to keep the Google Analytics separate later in Refactoring section of this article.

Introduction

I have invested some time over the past week in getting to know Google Analytics a lot better. After working through the "in-depth analysis" section over on Google Analytics training ground (called Conversion University) I decided to implement events to track some items that are not tracked by the basic out-of-the-box Analytics implementation.

When I woke up this morning I barely knew anything about jQuery having only used it once or twice before (each time with just enough of a gap for me to forget all the details when it came to using it again). Because writing the jQuery code was as much of a journey for me as the GA event tracking was, I will also cover a couple of tricks I learned along the way.

Just in case it hasn't occurred to you - GA is an abbreviation for Google Analytics.

What does the Event Tracking do that normal Analytics doesn't?

A good place to start this off is to justify what this piece of technology does and why we would want to use it. By tracking an event we can record visitor activities that don't equate to a page visit but are still valuable to us. For example, you might want to know stats on your PDF downloads or other downloadable assets.

A more advanced use would be to track a video player such as those embedded in the YouTube site. You could easily think of some usage patterns that you might want to track such as when the user presses play on the video. You could also track abandonment and make a new event if they watch over 90% of the video. Another example would be clicking on one of the recommended video links that are displayed after the main video has completed playing.

It is worth remembering that a maximum of 500 events can be tracked per user per visit so you shouldn't try to track events that happen very frequently.

You should now see that this is a potentially valuable feature to master.

_trackEvent

The way to track an event within Google Analytics is to call the _trackEvent() method on the _gaq object.

The _gaq object is part of the tracking code snippet that you will have pasted into your site (if you haven't setup a Google Analytics account yet then you should review this screencast which explains how to install your tracking code).

The method takes several parameters - category, action, label and value.

category string The category is a general category (in my code I have used "Sample Application" and "Image Enlargement" as my categories).
action string The action is a specific action for the event (such as "Download", "Click", "Play", "Vote", etc).
label string (optional) If you want to record something specific like the filename of the video being played then you can put this in the label parameter.
value integer (optional) This last type is if you want to assign a specific value to the event such as somebody you can make £5 in advertising each time somebody watches one of your videos.

A simple page tracker event might look like this:

_gaq.push(['_trackEvent', "Videos", "Play", "Cat falls off table.avi", 5]);

This would assign a value of 5 dollars and attribute it to a play of a video called "Cat falls off table.avi".

Planning your events

I just want to pass on a bit of advice that I picked up while learning in the Conversion University. You should create a planning document which outlines the general structure of what events you intend to capture. Using a straightforward naming convention and make sure you stick to it. Otherwise you will find it unmanageable when it comes to trying to make sense of the data collected.

Look at the code

Now that you understand the background, lets take a look at the code I have implemented in my blog to track these items and then afterwards I will go over the code highlighting the major features.

<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js' type='text/javascript'></script> 
<script type='text/javascript'>
/* <![CDATA[ */
$(document).ready(function () {
    SetupGoogleAnalyticsTrackEvents();
});

function SetupGoogleAnalyticsTrackEvents()
{
    TrackEventsForCodeSampleDownloads();
    TrackEventsForImageClicks();
}

function TrackEventsForCodeSampleDownloads()
{
    var FilterByBlogSampleHref = "href^='http://cid-15f731b049c8a797.skydrive.live.com/self.aspx/BlogExamples/'";
    var FilterByRarFileExtension = "href$='.rar'";
    
    $("a[" + FilterByBlogSampleHref + "][" + FilterByRarFileExtension + "]").click(function() {
        var SampleFileName = ExtractFileNameFromUrl($(this).attr("href"));
        TrackEvent("Sample Application", "Download", SampleFileName);
    });
}

function TrackEventsForImageClicks()
{
    TrackEventByFileExtension(".gif");
    TrackEventByFileExtension(".jpg");
    TrackEventByFileExtension(".jpeg");
    TrackEventByFileExtension(".png");
}

function TrackEventByFileExtension(FileExtension)
{
    $("a[href$='" + FileExtension + "']").click(function() {
        var ImageFileName = ExtractFileNameFromUrl($(this).attr("href"));
        TrackEvent("Image Enlargement", "Click", ImageFileName);
    });
}

function ExtractFileNameFromUrl(Url)
{
    // Note this code assumes
    //   - that the url doesnt contain a query string
    //   - that a real url has been passed in
    //   - that the url has a filename and isnt a folder
    var SplitUrl = Url.split('/');
    
    var FileName = SplitUrl[SplitUrl.length-1];
    
    return FileName;
}
        
function TrackEvent(Category, Action, Label)
{
   _gaq.push(['_trackEvent', Category, Action, Label]);
}
/* ]]> */
</script>

Pseudo code theory

Lets take a quick run through the concept behind this script so you can see why I have structured it the way I have. The main method is called from a jQuery ready event so if you have put your analytics tag just before a </head> tag then _gaq will be setup by the time this executes.

I have changed this from the original article so that it uses the Google CDN to download jQuery instead of the Microsoft CDN because of Dave Ward's article.

I have split it into two algorithms. The first one finds all the links on the page pointing to rar files on my Windows Live SkyDrive account which is where I keep all my sample applications. The second one plucks out links which are pointing to images.

The code registers click events with all of the links it finds matching these criteria. Then when you click on one of these links the click handler fires and calls the track event method.

Double where clauses

I needed to filter the links to my sample applications by two criteria. First it needed to start with my Sky Drive url and secondly it should end with a .rar extension.

While researching how to do this I found that you can chain up multiple [] where clauses. The code that uses this technique looks like this:

$("a[" + FilterByBlogSampleHref + "][" + FilterByRarFileExtension + "]");

After I had written the initial code I refactored out the specific filters into two variables. This was so that it was clear at a glance what I was trying to filter. This is important because when I come back to this code after a few months I don't want to have to decode what the filters mean.

Ends with operator

The other cool trick I found which shows the power of jQuery was the ends with operator. This looks like this:

$("a[href$='" + FileExtension + "']");

The $= means that the href attribute must end with the value of FileExtension. I didn't refactor this filter out because I felt that the name of the method made it clear we are looking at the end of a url.

Layout of the code (Refactored)

After I had finished the code this looked quite a lot different than it does now. I made a series of small changes to the code to ensure that the code was as clean as possible. For example, I put the _trackEvent call in a wrapper so that I didn't have third party library calls throughout my code. If it turns out in the future that I need to switch my analytics provider then I will only have to change this code in one place.

I extracted the code in TrackEventByFileExtension() so that I could reduce duplication in my code.

I put the ExtractFileNameFromUrl() code in its own function to improve the readability of its calling function by reducing the complexity of the parent method. The name of the method in the code also serves as a form of human readable documentation which explains what the code is doing in natural language.

How to use this in your projects

The sample code that I have presented above is really tailored to my specific event tracking requirements (which I completely fabricated so that I had something to track).

Even if you have only a limited understanding of javascript I think this should provide you with enough information to pull out the bits you need to track your own custom events automatically.

If you are putting this on your own website then you should also consider putting this in an external file. The only reason I have put the code inline in the head tag is because my blog is a hosted solution and I don't have file space setup to store it in.

More information

kick it Shout it

3 comments:

andry yudha said...

i wanna try this,,,

thanks for your info :D

André Mafei said...

Very good post! Thanks man.

André Mafei
São Paulo, Brazil

Matt Bernhardt said...

Thanks for this writeup! Very helpful as I tried to implement something similar at my institution.