Track Downloads and Other Click Events in Analytics with jQuery

jquery-analytics-event-trackingThe old way to track clicks and outbound links in Google Analytics was to call the _trackPageview function on the click event, passing it a label that could then be searched for in Analytics. One of the problems with this method was that the clicks were counted as pageviews, affecting your total pageview number.

Now Analytics offers an event tracking feature. It can be used to track downloads, outbound links, mailto links, AJAX events—any click event. But the event tracking function still has to be implemented for each event you want to track. Here’s some jQuery that handles the implementation for you.

How it works

If you want to track the number of downloads for a file, give the link a class of “trackDownload”. If you want to track clicks on a mailto email link, use a class “trackEmail”. For outbound links, the class is “trackOutbound”.

If you want to specify a label for the event, you can use the rel attribute. If a rel value is not present, the filename (for downloads), URL (for outbound links), or email (for mailto) will be used.

A walk through the code
$(document).ready(function(){	
 
	//Bind tracking classes
	$(".trackDownload,.trackOutbound,.trackEmail").click(function(){
 
		var eventType = this.className,
			relValue = $(this).attr("rel"),
			eventCategory;

First we bind the class names to our function. Then we store the class name and the rel value.

//Check if rel attribute is set. If so, use the rel value as the event label.
		if (relValue.length) {
 
			var eventLabel = relValue;
 
			if (eventType == "trackDownload") {
				eventCategory = "downloads";
			} else if (eventType == "trackOutbound") {
				eventCategory = "outbound";
			} else {
				eventCategory = "mailto";
			}
		}

Now we check to see if a rel value exists. If one does, we use the rel value for the event label. We then assign the event category based on the class name.

//Otherwise we'll use the URL or filename as the event label.
		else {	
 
			var trackURL = $(this).attr("href"),
				eventLabel;
 
			if (eventType == "trackDownload") {
				eventLabel = trackURL.substr(trackURL.lastIndexOf("/") + 1);
				eventCategory = "downloads";
			} else if (eventType == "trackOutbound") {
				eventLabel = trackURL;
				eventCategory = "outbound";
			} else {
				eventLabel = trackURL.substr(7);
				eventCategory = "mailto";
			}
		}

If a rel value is not set, we store the HREF value and then define the event label based on the event type (the class name). For file downloads, the filename is used as the label. For outbound links, we use the full URL. For mailto links, we use the email address.

//Send the event to Analytics
		pageTracker._trackEvent(eventCategory, 'click', eventLabel);		
 
	});
});

Lastly, we send the event data off to Google.

This is just one possible solution. If you wanted to use the class names as the event category names, the code could be reduced by about half. (Update: I’ve provided code for this option at the end of the post.)

Another option would be to have the jQuery automatically track all outbound, download, and external links, without having to tag the links you want to track with classes. There are a few plugins out there that do this. The problem with the ones I saw was that you end up having to run a bunch of regex on every anchor link on every page load. This isn’t a major problem, but in my case, where I didn’t want to track every outbound link anyway, I felt it was unnecessary overhead.

Complete code and file below. Note: If you track links that use a rel value for another purpose, the rel value will get passed in as the event label. I have a solution for this but I’ve left it out of this version because I’m not sure how common this situation is.

Complete Code
$(document).ready(function(){	
 
	//Bind tracking classes
	$(".trackDownload,.trackOutbound,.trackEmail").click(function(){
 
		var eventType = this.className,
			relValue = $(this).attr("rel"),
			eventCategory;		
 
		//Check if rel attribute is set. If so, use the rel value as the event label.
		if (relValue.length) {
 
			var eventLabel = relValue;
 
			if (eventType == "trackDownload") {
				eventCategory = "downloads";
			} else if (eventType == "trackOutbound") {
				eventCategory = "outbound";
			} else {
				eventCategory = "mailto";
			}
		}
		//Otherwise we'll use the URL or filename as the event label.
		else {	
 
			var trackURL = $(this).attr("href"),
				eventLabel;
 
			if (eventType == "trackDownload") {
				eventLabel = trackURL.substr(trackURL.lastIndexOf("/") + 1);
				eventCategory = "downloads";
			} else if (eventType == "trackOutbound") {
				eventLabel = trackURL;
				eventCategory = "outbound";
			} else {
				eventLabel = trackURL.substr(7);
				eventCategory = "mailto";
			}
		}
 
		//Send the event to Analytics
		pageTracker._trackEvent(eventCategory, 'click', eventLabel);		
 
	});
});
Update

Here’s a simplified version for those who don’t want to specify a label for each event. It does away with the rel tag, and also uses the class names “trackDownload, etc” as the category name in Analytics.

$(document).ready(function(){	
 
	//Bind tracking classes
	$(".trackDownload,.trackOutbound,.trackEmail").click(function(){
 
		var	eventCategory = this.className,
			trackURL = $(this).attr("href"),
			eventLabel;
 
		if (eventCategory == "trackDownload") {
			eventLabel = trackURL.substr(trackURL.lastIndexOf("/") + 1);
		} else if (eventCategory == "trackOutbound") {
			eventLabel = trackURL;
		} else {
			eventLabel = trackURL.substr(7);
		}
 
		//Send the event to Analytics
		pageTracker._trackEvent(eventCategory, 'click', eventLabel);		
 
	});
 
});
Referenced

UN2WFXZ3GH3F

Both comments and pings are currently closed.

Discussion

This is some highfalutin stuff you’ve got here! I’m just now becoming able to visualize the DOM and what it means to manipulate it, and reading complex but highly-efficient jQuery like this blows my mind-grapes! Nicely done, sir!

I wrote a little plugin for that purpose ;-) You can check it out at github: http://github.com/glamorous/jQuery-Google-Analytics

Feel free to fork it!

Jonas, thanks for the link. Your plugin looks excellent. Much more respectable than the few lines of code I threw together. =)

Do you have this running on any live sites?

This is a great technique! I’ve done similar things and this paired with Googlytics’ new asynchronous loading method (http://googlecode.blogspot.com/2009/12/google-analytics-launches-asynchronous.html) it seems like we can now get very nice performance with Analytics.

One performance thing I noticed though is the use of .bind(). Given that this is to be used on general HTML pages where the number of links to be tracked could theoretically be very high (e.g. a page like http://www.mozilla.com/en-US/firefox/all.html), shouldn’t this instead be use event binding via .live()?

That way, instead of having to iterate over every single element that matches these classes and initialize and bind an event handler to each, it would simply bind one single handler to the document object and handle all events bubbling up the DOM. This would be both faster in setup and less memory intensive in usage.

The one gotcha is that “this” will no longer be valid, instead it’d have to be changed to the “target” property of the event object.

What do others think here?

Hey Phil, I think you’re absolutely correct. Since we’re binding multiple classes to the same handler, the .live() method, from a performance perspective, would be the smarter approach.

My only concern is that .live(), as you said, relies on the event bubbling up to the document level. If an ancestor of our tracking element has a click event handler using return false, the bubbling chain is terminated before the event reaches the document. Ben Nadel has a nice post about this, where 2 solutions are discussed: using a mousedown event instead of click, and using preventDefault instead of return false.

How is “this” affected by using .live() in place of .bind()? I was able to replace bind with live in the aforementioned code and everything worked as before.

Thanks for your comments!

I’d have to sit down with it for a bit to check, but in my experience you often need to use the “target” prop of the event object passed to the handler (e.g. $(‘foo’).live(‘click’, function(event) { /* … */ $(event.target).doStuff(); }); ) to ensure that the event is handled correctly every time. My understanding being that — at least sometimes, sorry that I don’t have a precise explanation here — the scope the function will run in is the document and thus “this” will point to the document object.

It’s entirely possible that I’ve been missing something in my projects though. I’ll look into this when I’ve got free time later tonight.

David

hi,

Do you have an updated plugin for using with asynchronous google analytics?

David,
Yes! For an updated version that uses the async implementation, see this post.