Web Development Blog

Using Google Analytics Event Tracking

One of the first things a new website owner wants to know is how people are visiting my site. Any statistics package can give you this information but my current favorite would be Google Analytics, it free, flexible and easy to use. Page views and bounce rates are not always a good measure of success. Plenty of people may visit the site but how many take action and contact you or sign up for the service you provide. This is where event tracking a little known feature of Google Analytics comes in it you to track actions your visitors take on your site and manipulate the results.

Basic event tracking can be used to track how many people click on a link that can't be tracked by Analytics such as

  • downloading a file such as a PDF
  • a link to an external site i.e. tracking exits to partner sites
  • interaction with AJAX components of a site that do not reload the page
  • actions generated from inside a flash component
  • tracking conversions such as visitors submitting a contact form or visitors clicking on email links i.e. who has clicked on an email link and contacted be after viewing a page on my site and what page was that.
  • tracking conversion types i.e. what topics are people contacting me about
  • tracking conversion failure i.e. visitors who did not complete a signup form or when to a shop check out and did not complete the checkout

Of all the points I think the most interesting is tracking conversions and conversion failure. I will illustrate a common conversion that is of interest to most business sites, tracking contacts from your websites contact form.

Setup your contact form

For this example I have a basic contact form you may see on an accommodation site with a few fields Name, Email, Accommodation and Question. Obviously this can be any sort of contact form. I am going to extend the tracking to include what sort of accommodation the visitor was contact you about. You may say well I know already because they contacted me, however with Google Analytics you can easily segment these visitors and compare them against all your visitors or another segment.

Add the JavaScript to your form

In this example I am using the older Google tracking code as the demo site is setup with that. You can see in I have added some JavaScript to execute on the submit button click event. It includes in the Event Label field the value of the Accommodation dropdown. Doing this will allow us to compare the volume of enquiries of each accommodation type.

    <table> 
        <tbody> 
            <tr> 
                <td> 
                    <label id="LabelName" for="Name"
                        Name</label>
                </td> 
                <td> 
                    <input type="text" name="Name"/> 
                </td> 
            </tr> 
            <tr> 
                <td> 
                    <label id=" LabelEmail" for=" Email"
                        Email</label>
                </td> 
                <td> 
                    <input type="text" style="width: 200px;" id="Email" name="Email"/> 
                </td> 
            </tr> 
            <tr> 
                <td> 
                    <label id="LabelAccommodation" for="Accommodation"
                        Accommodation</label> 
                </td> 
                <td> 
                    <select id="Accommodation" name="Accommodation"
                        <option value="Suite">Suite</option> 
                        <option value="Studio">Studio</option> 
                        <option value="Hostel">Hostel</option> 
                    </select> 
                </td> 
            </tr> 
            <tr> 
                <td> 
                    <label id="LabelQuestion" for="Question"
                        Question</label> 
                </td> 
                <td> 
                    <textarea id="Question" cols="20" rows="4" name="Question"></textarea> 
                </td> 
            </tr> 
            <tr> 
                <td> 
                </td> 
                <td> 
                    <input type="submit" id="Submit" onclick="pageTracker._trackEvent('Form', 'Enquiry', Accommodation.options[Accommodation.selectedIndex].value);" 
                        value="Submit" name="Submit"/> 
                </td> 
            </tr> 
        </tbody> 
    </table> 
 

Upload your code and wait for some visitors to use your contact form or test it yourself. Once the data appears in Analytics you can start on the next step.

Create a segment of your visitors

This can be done by clicking on the Advanced Segments link in the left of Google Analytics. Create a new segment and give it a sensible name, in this case I’m using ‘Visitors with an enquiry’. Drag the event action from the left and drop it on the right and choose the condition ‘Matches exactly’ and then the value ‘Enquiry’ and save your segment.

 

View the report

You could create a custom report or customize the report on your dashboard. Expand the ‘Advanced Segments’ section on the top right of your main dashboard graph and select your custom segment ‘Visitors with an enquiry’ to compare.

 You can now see a comparison of visitors vs. visitors who make an enquiry.

 

Extending to track failed conversions

I want to know how many people when to the contact page but did not use the contact form. We need to create a new segment to single out visits to the contact page. So create a new segment of visitors to the contact page. In a real world situation you may want to be more specific than just visitors to the contact page as someone may have gone there for a phone number. A better comparison may be those who have gone to the contact page and started filling out the form but not pressed the submit button. If so you would need to attach some JavaScript to trigger an event that occurred when a field was filled out then create a segment from that.

Comparing two custom segments

Again expand the ‘Advanced Segments’ section on the top right of your main dashboard graph and select your custom you want to compare.

You can now see the difference between your two custom segments the difference being the number of visitors that failed to be converted to leads. The same method can be used with steps to track conversion failure in an application form or shopping cart checkout.



Fix for Flash horizontal 1px pixel offset in Firefox 3.6

After updating to Firefox 3.6 on OSX I discovered many of the websites that use flash had the flash object offset by 1 pixel horizontally. Its a real pain hopefully it will be fixed soon. In the mean time I have discovered a temporary fix you can make to your HTML.

I researched the problem I found the bug that had been reported - Bug 550246. Still it may be some time off until it is fixed and released. The bug occurs when you have a flash element nested inside a div where you have the div aligned to the center using margin-left: auto; margin-right: auto. On OSX in Firefox the flash in the example below would be misaligned by 1px.
 

Example

 
<div style="width: 500px; margin-left: auto; margin-right: auto"
    <object width="500px" height="200px" type="application/x-shockwave-flash" data="gallery.swf" style="visibility: visible;"
       <param name="wmode" value="transparent"
       <param name="play" value="true"
       <param name="quality" value="high"
       <param name="loop" value="true"
       <param name="menu" value="false"
    </object> 
<div> 

I have a section of a screen shot here to demonstrate the problem where the flash is out by one pixel.



 

The Fix


The ideal fix for the problem is the Firefox developers fix the bug but in the mean time I have discovered a way you can hide the problem. As discussed in the bug it appears to be a error in calculation when applying center alignment. So my solution is to simply offset the div element by 1 pixel which allows the flash to be in the correct spot when the browser is maximized. As detailed in the bug report if you re-size the browser window and the viewport width goes from even to odd you will see the offset appear and disappear. I have made another screen shot to demonstrate the fix. In this example I have added a 1 pixel border to the div, I have made it red so you can see it other wise make it transparent on a real site.




Changes to make to the Code

<div style="border-left: 1px solid transparent; width: 500px; margin-left: auto; margin-right: auto"
    <object width="500px" height="200px" type="application/x-shockwave-flash" data="gallery.swf" style="visibility: visible;"
       <param name="wmode" value="transparent"
       <param name="play" value="true"
       <param name="quality" value="high"
       <param name="loop" value="true"
       <param name="menu" value="false"
    </object> 
<div> 

So simple enough and it should work on any site until the bug (Bug 550246 - Centered DOM elements shift 1 pixel depending on EVEN or ODD browser width) is fixed. I think it worth while doing even though it only works when the with of the browser in pixels is an even number as it could be expected you have your browser set to use the maximum with of your screen which is an even number.

Hope this helps






JQueryDialog wrapper for C# ASP.NET

I wanted to integrate the JQuery UI Dialog with a ASP.NET project so I created a C# class to wrap the functionality and make it easy to configure. I ended up not using the Dialog in the end the class was never completed, but thought I would post it in case its is useful for someone.

JQuery UI Dialog Class

using System; 
using System.Text; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using System.Collections; 
using System.ComponentModel; 
using System.Web.UI.Design; 
using System.Web.UI.Design.WebControls; 
using System.ComponentModel.Design; 
 
namespace Juna.Web.UI.WebControls 
    /// <summary> 
    ///  
    /// </summary> 
    public enum JQueryDialogPosition 
    { 
        /// <summary> 
        ///  
        /// </summary> 
        Center, 
        /// <summary> 
        ///  
        /// </summary> 
        Left, 
        /// <summary> 
        ///  
        /// </summary> 
        Top, 
        /// <summary> 
        ///  
        /// </summary> 
        Bottom, 
        ///<summary> 
        ///</summary> 
        Right 
    } 
 
    /// <summary> 
    ///  
    /// </summary> 
    [Serializable] 
    public class JQueryDialogButton 
    { 
        /// <summary> 
        ///  
        /// </summary> 
        public string Text { getset; } 
        /// <summary> 
        ///  
        /// </summary> 
        public string CssClass { getset; } 
        /// <summary> 
        ///  
        /// </summary> 
        public string OnClientClick { getset; } 
    } 
 
    /// <summary> 
    ///  
    /// </summary> 
    [Serializable] 
    public class JQueryDialogButtonCollection : CollectionBase 
    { 
        /// <summary> 
        /// Gets or sets the <see cref="Juna.Web.UI.WebControls.JQueryDialogButton"/> with the specified obj. 
        /// </summary> 
        /// <value></value> 
        public JQueryDialogButton this[object item] 
        { 
            get { return (JQueryDialogButton)List[IndexOf(item)]; } 
            set { List[IndexOf(item)] = value; } 
        } 
 
        /// <summary> 
        ///  
        /// </summary> 
        /// <param name="item"></param> 
        /// <returns></returns> 
        public int Add(JQueryDialogButton item) 
        { 
            return List.Add(item); 
        } 
 
        /// <summary> 
        ///  
        /// </summary> 
        /// <param name="index"></param> 
        /// <param name="item"></param> 
        public void Insert(int index, JQueryDialogButton item) 
        { 
            List.Insert(index, item); 
        } 
 
        /// <summary> 
        ///  
        /// </summary> 
        /// <param name="item"></param> 
        public void Remove(JQueryDialogButton item) 
        { 
            List.Remove(item); 
        } 
 
        /// <summary> 
        ///  
        /// </summary> 
        /// <param name="item"></param> 
        /// <returns></returns> 
        public bool Contains(JQueryDialogButton item) 
        { 
            return List.Contains(item); 
        } 
 
        /// <summary> 
        /// Copies to. 
        /// </summary> 
        /// <param name="array">The array.</param> 
        /// <param name="index">The index.</param> 
        public void CopyTo(JQueryDialogButton[] array, int index) 
        { 
            List.CopyTo(array, index); 
        } 
 
        /// <summary> 
        ///  
        /// </summary> 
        /// <param name="item"></param> 
        /// <returns></returns> 
        public int IndexOf(object item) 
        { 
            if (item is int
            { 
                return (int)item; 
            } 
 
            if (item is string
            { 
                for (int i = 0; i < List.Count; i++) 
                { 
                    if (((JQueryDialogButton)List[i]).Text == item.ToString()) 
                    { 
                        return i; 
                    } 
                } 
                return -1; 
            } 
            throw new ArgumentException("Only a string or an integer is permitted for the indexer."); 
        } 
    } 
 
    /// <summary> 
    ///  
    /// </summary> 
 
    [ParseChildren(true)] 
    [Designer(typeof(JQueryDialogControlDesigner))] 
    public class JQueryDialog : CompositeControl 
    { 
        private bool? _open; 
        private bool? _enable; 
        private bool _moveToTop; 
 
        /// <summary> 
        ///  
        /// </summary> 
        public JQueryDialog() 
        { 
            Style[HtmlTextWriterStyle.Display] = "none"
        } 
 
        /// <summary> 
        /// Gets the jQuery refernence to the opbect i.e. $('#Dialog1') 
        /// </summary> 
        [Browsable(false)] 
        public string ClientReference 
        { 
            get 
            { 
                return string.Format("$('#{0}')", ClientID); 
            } 
        } 
 
        /// <summary> 
        /// Specifies which buttons should be displayed on the dialog 
        /// </summary> 
        [PersistenceMode(PersistenceMode.InnerProperty)] 
        public JQueryDialogButtonCollection Buttons 
        { 
            get 
            { 
                if (ViewState["Buttons"] == null
                { 
                    ViewState["Buttons"] = new JQueryDialogButtonCollection(); 
                } 
                return (JQueryDialogButtonCollection)ViewState["Buttons"]; 
            } 
        } 
 
        /// <summary> 
        /// When autoOpen is true the dialog will open automatically. If  
        /// false it will stay hidden until Open is called. 
        /// </summary>