In todays online world it is common for web developers to improve their websites by enabling a large amount of interactive functionality. Often this takes the form of JavaScript applications that no longer adhere to the simple page-by-page model but instead act more like a desktop program (Gmail, YouTube and Twitter for example).

If many interactions are happening in a single page without the URL changing, this experience is great for the user but can prove problematic when tracking for analytics. Traditionally analytics software such as Google Analytics is included in every web page of a site and each time a page is downloaded it is tracked back to Google. Obviously if the visitor never leaves that URL, this causes the aforementioned issue.

Google have a solution called "Events" that allow a developer to programmatically track an interaction via JavaScript. Events breakdown into the following components:

  • Category
  • Action
  • Label (optional)

For example, to track a customer checking out of an online store through a JavaScript form interface the following events may be used:

Format: Category / Action / Label

  1. Checkout / ViewCart
  2. Checkout / Country Selected / USA
  3. Checkout / Details Provided
  4. Checkout / Order Confirmed

From these tracked events, reports can be generated or funnels (called "Goals" by Google) setup to track conversion rates.

The method that Google provides to programmatically track an event looks as follows:

_gaq.push(['_trackEvent', 'Checkout', 'Country Selected', 'USA']);

This is simple enough, however, it is not wise to litter application code with calls to _gaq. If the Analytics script does not load due to a network issue this variable will be undefined and cause a potentially serious error. Also, if you someday remove Google Analytics it may be problematic to remove all occurrences of this throughout your code base.

For this reason I have created the following wrapper around the tracking code with a simple API and multiple advantages that can be plugged into any JavaScript application.

$.gaevents = {
    /* If true, will send request to google, if false will log to console */
    liveTracking: true,

    /**
     * Track an event with the given parameters
     * If category or action are not defined, nothing will happen
     */
    track: function( category, action, label, noninteraction ) {
        var active, varsDefined, present, eventArray;

        // Check the Google Analytics script is present
        active = ( typeof _gaq !== "undefined" );

        // Category and action are required fields, check this now and exit if not true 
        varsDefined = ( typeof category !== "undefined" && typeof action !== "undefined" );
        present = ( category && action );
        if ( !active || !varsDefined || !present ) { return;}

        eventArray = [ '_trackEvent', category, action ];

        // Only add the label if it is present
        if ( typeof label !== "undefined" && label ) {
            eventArray.push( label );
        }

        // By default ignore this, if true however "bounce rate" will not be affected
        if ( typeof noninteraction !== "undefined" && noninteraction === true ) {
            eventArray.push( true );
        }

        // Send event to Google
        if ( this.liveTracking ) {
            _gaq.push( eventArray );
        } else {
            if ( typeof console !== "undefined" && console.log ) {
                console.log( "GA Event: ", eventArray );
            }
        }

        return true;
    },

    /* Assign a callback function that is fired once Google has collected the event */
    setHitCallback: function( callback ) {
        _gaq.push( [ '_set', 'hitCallback', callback ] );
    },

    enableDevMode: function() {
        this.liveTracking = false;
    }
};  

I have added the wrapper to the jQuery $ namespace but this could easily be removed if need be. Tracking can be performed at any time in the following format:

$.gaevents.track("Checkout", "Country Selected", "USA");

The API is easier to read and has the advantage that if Google update their code (e.g. add additional parameters) the tracking method can be updated in a single location with confidence.

The wrapper also has the advantage that if the analytics script does not load, no error will occur. If analytics loads later (due to a slow network) any calls to track() will begin to take effect automatically.

Additionally, the wrapper provides an enableDevMode() method that will simply log your event to the browser console instead of sending to Google whilst developing or testing.

$.gaevents.enableDevMode(); // Run this before any "track()" calls

For full details on Google Analytics event tracking, please see the official documentation

Edit (16/08/2013): As is stated in the comments, there is a potential issue where a form submission, or link click will unload the browser window before the event can be sent to the Google servers. To fix this, Google provide a "hitCallback" API that will fire a callback once the event has been collected. This can be configured using:

$.gaevents.setHitCallback( function() {
    // Here you manually submit the form and trigger the page change
    $myForm.submit(); 
} );