Blast Analytics and Marketing

Analytics Blog

Supporting Leaders to EVOLVE
Category: Google Analytics

How to Track Downloads & Outbound Links in Universal Analytics

September 4, 2013

By default Google Analytics does not track file downloads, email, telephone or other outbound link clicks automatically. We developed dynamic link tracking code a couple years ago to address this and due to the popularity, we revisit it from time to time as the Google Analytics platform evolves.

The latest evolution is Universal Analytics and we have updated the code we provided in our previous ‘How to Track Downloads & Outbound Links in Google Analytics’ blog post to use the new Universal Analytics syntax. The recent version of the tracking code includes an additional feature that leverages the NEW hit callback functionality.

If you are not yet familiar with Google’s Universal Analytics, we encourage you to read a brief case study, ‘Yellow Pages Group New Zealand Upgrades to Universal Analytics‘, about one of our Google Analytics Premium clients who is utilizing Universal Analytics. The post outlines the benefits of the new platform, as well as, how we leverage and extended Universal Analytics to meet their unique business needs.

Dynamically Track Downloads & Other External Links

The code below will dynamically detect clicks on file downloads and external links. As before, this code requires the jQuery JavaScript library to be set before the code.

The code can be easily modified to suit your needs. We recommend placing the code in its own .js file. This script automates the following:

  • Tracks file downloads as events for the following extensions: .zip, .exe, dmg, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .txt, .rar, .wma, .mov, .avi, .wmv, .flv, .wav (again feel free to modify the list)
  • Tracks outbound clicks as events if the href value contains http:// or https:// and the domain value doesn’t match the current domain
  • Tracks mailto email clicks
  • Tracks Tel telephone clicks

In addition, the code takes advantage of a new Google Universal Analytics callback feature to determine when the hit request finishes sending data. This is especially useful in cases where the link does not open a new window, since the hit request needs time to finish before taking the user to their destination. This eliminates the requirement of putting a timeout in place to delay the click longer than it needs to be delayed (or quicker than it should on high latency connections).

[sourcecode language="js"]<script type='text/javascript'>
if (typeof jQuery != 'undefined') {
	var filetypes = /\.(zip|exe|dmg|pdf|doc.*|xls.*|ppt.*|mp3|txt|rar|wma|mov|avi|wmv|flv|wav)$/i;
	var baseHref = '';
	if (jQuery('base').attr('href') != undefined) baseHref = jQuery('base').attr('href');
	var hrefRedirect = '';

	jQuery('body').on('click', 'a', function(event) {
		var el = jQuery(this);
		var track = true;
		var href = (typeof(el.attr('href')) != 'undefined' ) ? el.attr('href') : '';
		var isThisDomain = href.match(document.domain.split('.').reverse()[1] + '.' + document.domain.split('.').reverse()[0]);
		if (!href.match(/^javascript:/i)) {
			var elEv = []; elEv.value=0, elEv.non_i=false;
			if (href.match(/^mailto\:/i)) {
				elEv.category = 'email';
				elEv.action = 'click';
				elEv.label = href.replace(/^mailto\:/i, '');
				elEv.loc = href;
			else if (href.match(filetypes)) {
				var extension = (/[.]/.exec(href)) ? /[^.]+$/.exec(href) : undefined;
				elEv.category = 'download';
				elEv.action = 'click-' + extension[0];
				elEv.label = href.replace(/ /g,'-');
				elEv.loc = baseHref + href;
			else if (href.match(/^https?\:/i) && !isThisDomain) {
				elEv.category = 'external';
				elEv.action = 'click';
				elEv.label = href.replace(/^https?\:\/\//i, '');
				elEv.non_i = true;
				elEv.loc = href;
			else if (href.match(/^tel\:/i)) {
				elEv.category = 'telephone';
				elEv.action = 'click';
				elEv.label = href.replace(/^tel\:/i, '');
				elEv.loc = href;
			else track = false;

			if (track) {
				var ret = true;

				if((elEv.category == 'external' || elEv.category == 'download') && (el.attr('target') == undefined || el.attr('target').toLowerCase() != '_blank') ) {
					hrefRedirect = elEv.loc;

					ga('send','event', elEv.category.toLowerCase(),elEv.action.toLowerCase(),elEv.label.toLowerCase(),elEv.value,{
						'nonInteraction': elEv.non_i ,

					ret = false;
				else {
					ga('send','event', elEv.category.toLowerCase(),elEv.action.toLowerCase(),elEv.label.toLowerCase(),elEv.value,{
						'nonInteraction': elEv.non_i

				return ret;

	gaHitCallbackHandler = function() {
		window.location.href = hrefRedirect;

The script sets download, email and tel link clicks as interaction events while the external site clicks are non-interaction, which can be adjusted as needed.

Detailed Download & External Link Reports

Once you have implemented the new tracking code you will be presented with well organized event data around file downloads, external link, email link, and telephone link clicks. These reports are especially useful to determine the effectiveness of your file downloads and various links.


If you would like to see the most popular downloads, you can do so by drilling down into the event category and selecting event label as the primary dimension. Here is a download report example:

Let us know if you have any questions about this new evolution of the outbound link and download file tracking code, or if you want to know more about Google Universal Analytics in general.

  • Bronwyn V

    Not sure why, however I can’t for the life of me get this script work,

    I wasn’t sure if it was due to the version of jquery i was using, so i tried both 2.0.3 and 1.10.2 just incase

    I added jquery via google libraries and added the above code after it in its own js file

    Tested it with a download link to a file and an external link and nada, zip, zilch.

    adding an id to a link and using the following jquery at the bottom of the page instead, sends an event without issue

    jQuery(‘#yup’).on(‘click’, function() {
    ga(‘send’, ‘event’, ‘links’, ‘linkclick’, ‘exitlinks’);

    Any hints on what I may be doing wrong would be hugely appreciated

    Thanks 🙂

    • Hello Bronwyn,

      I’m sorry you’re having problems with the script. It’s really hard for me to troubleshoot your problem since I don’t see your code. I know this response is a little dated but in case you have not yet resolved this, please send me a link and I’d be more than happy to help –


      • Bronwyn V

        oh my gosh Olaf, sooo sorry, had lost track of this. I did get this working, I cant remember what the exact issue was, however it was some funkiness in my site code.. not related to your script.

        Thanks :-))

  • drgs100

    Hi Ryan, many thanks for this been a great help. Unfortunately I have not got this version working so instead I used a recommendation from the comments of the previous post to replaced the _gaq.push line with:

    ga(‘send’, ‘event’, elEv.category.toLowerCase(), elEv.action.toLowerCase(), elEv.label.toLowerCase(), elEv.value, elEv.non_i);

  • disqus_wKSpp72Chl

    Does this script work – seems to be some issues by the comments below? Please advise.

  • Hello Hannah,

    If you’re using WordPress, I would suggest using a plugin for GA., it’s a really good one and it does download tracking automatically. Otherwise you’ll need to modify your template via php and that can get a bit tricky –

  • loldroid

    doesn’t work me either. I tested it with manually adding
    ‘onclick=”ga(‘send’,’event’,’category’,’click’,’label’);”‘ to a link and it worked.

  • Johan

    I’m using jQuery 1.9.1 and isn’t working for me either. Should this script be loaded after the GA tracking script?

    • Ryan Chase

      Hi Johan,

      I tested this using jQuery 1.9.1 and it is working. The script should appear after your GA tracking code and your reference to jQuery,

  • Harold R

    I am not able to get this script working also. Have converted to Universal Analytics and added this link to JQuery.
    Is this correct?

  • Janek Kowalski

    Hej, It looks great! But it does not work in Chrome 32. Look like it blocks clicks – prevent default action. FF and Opera OK.

    • Ryan Chase

      Hi Janek,

      I tested this in Chrome 32 and it was working. What version of jQuery are you using?

      • Janek Kowalski

        jquery-1.10.2.js , I have had to remove ret = false

        from :

        if((elEv.category == ‘external’ || elEv.category == ‘download’) && (el.attr(‘target’) == undefined || el.attr(‘target’).toLowerCase() != ‘_blank’) ) {
        hrefRedirect = elEv.loc;

        because all links stopped working.

  • Joel Lowther

    Just a quick note. You will want to apply this code just above the /body tag. Putting it below the universal code and right above the /head tag will not allow the code to function.

    Another quick side note for those who don’t use Google Analytics very often, use the Real-Time > Events option to see if the code is working.

    • Art Thompson, Jr.

      This was the snag for me, too. I’m glad I found this comment, otherwise I would still be pulling what’s left of my hair out.

    • Greetings,
      since I had some issues, here is what worked for me:
      1.) Import JQuery in your body first
      2.) Place Universal Analytics code above /head tag
      3.) Put ga(‘send’, ‘event’, elEv.category.toLowerCase(), elEv.action.toLowerCase(), elEv.label.toLowerCase(), elEv.value, elEv.non_i); within your desired ‘a’ tags (it is working with onclick event)
      4.) Place script from this site just above /body tag, as said in comment from Mr. Joel Lowther!
      Hope this helps!

      • Ben

        In step #3, are you saying we need to add an onclick event to any link we want to track with the script?

  • sonofray

    I’m getting an error in Dreamweaver “There is a syntax error on line 3.” (below). Should I be concerned? I don’t have it working yet.

    Line 3:
    var filetypes = /.(zip|exe|dmg|pdf|doc.*|xls.*|ppt.*|mp3|txt|rar|wma|mov|avi|wmv|flv|wav)$/i;

    • sonofray

      Nevermind. I forgot to remove the script tags at the beginning and end when I made it a remote script. Heh.

  • Aaron Weiner

    Ryan, thank you very much for this blog post. I finally got the code to work by placing it right after the opening body tag. Thanks!

  • John Hoecker

    For those using Universal Analytics with WordPress, I dropped the event tracking script into header.php just below the body tag and it is working for tracking downloads (haven’t checked for others types of events – I have an audio player that I’d like to track “play” clicks as well).

    I suspect the cleaner way is to create a function, but that is beyond my paygrade…

    Thanks Ryan!!

  • Aaron Weiner

    Ryan, could this code be implemented through the Google Tag Manager? If so, how would you do that?

  • Southeast Community College Ne

    Ryan, thanks for updating the code to work with universal analytics. I see the events being recorded while in Real-Time, but there is never any data for the view. No category, etc. Any idea what could cause that?

  • Thanks so much. This script is so useful.

  • Louise

    Does the “manual inline approach” referred to in the previous version of this article still work?

  • NZ101

    Bit of an amateur query – but how do I activate the above on my Squarespace account? I can add code, but I’m not familiar with activating the above code through javascript – where/how do I host it and how do I link that to my site. Main goal is to track outbound links.

  • FreelanceWebdesignerNL

    Hey Ryan,

    Thanks for sharing this, i made my own but this is more complete.

    The only thing is that i am testing it now and i see in the real time overview that events are triggered. But it is not showing any information from these events, like category, action & label.

    Hope you can help.

    Kind regards,


    • Hello Robin,

      I’m not sure why you weren’t seeing it in Real Time but removing the nonInteraction parameter will change how that hit is handled when it comes to bounce rate for the page.

      This is something you can definitely adjust across all three types of events we’re capturing here (external, download, email) However for an external click, we usually like to keep it as an non interaction hit so that clicks to external links don’t affect the bounce rate for that page. In the case of a download, we make it an interaction click so that people who download are not counted the same as people who do not download when trying to figure out the bounce rate.

      Example. You have a page with a download and an exit link.

      Typically, when the person gets there, if they click on the download and we fire an event, the event is the page’s second hit and thus is disqualified as a bounce even if the user leaves the page without any further action. This is the desired logic since we want to exclude pages where people downloaded an item from being counted as bounces.

      For an external click, we want to reverse the logic since you typically do not track external links and having no interaction on the page then clicking on an external link is in fact a bounce. For this reason we pass that event the non-interaction value of true, so that the hit is ignored as an interaction.

      You may want to recheck your code and make sure there isn’t something else that caused it to fail to work.


      • Sheela

        Hi Olaf,

        I am desperately trying to make this code to work but it isn’t working for me at all. We are on expression engine platform and use google UA . I have embedded the css, js and jquery libs file on the head tag and then added the google snippet and below that is the event tracking code just before the tag. It doesn’t work !

  • Tim Young

    I can not seem to get this script to work. I am using jquery 1.44.

    The old script I used withing the page

    I thought the new script would be something like this
    ga(‘send’, ‘trackDownload’);
    ga(‘send’, ‘trackOutbound’);

    What I would really like to see the the full URL of the document being downloaded and I am getting nothing.

    I have tried placing the call to the J.S file before the GA code and after. Still nothing.

    Please help

    • Hello Tim,

      I’m not sure where you get _gaq.push([‘_trackDownload’]); and the outbound version from.

      What you need to do is place the above code on the page, usually via an external javascript and across all pages along with your standard GA code.

      If you cannot update your jQuery then you’ll need to change line 8 to:

      jQuery(‘a’).click( function(event) {

      I would suggest you update your jQuery though –

      Aside from that, you don’t need anything else. This listener will capture click on anchor tags and analyze if they’re email, outbound or downloads, then fire the event.


      • Hi Guys,

        What is the absolute lowest version of jQuery that this script will work on? The site is pretty old. On one part of the site we are running 1.2.2 & 1.7.2


        • Tony Catchpole

          I am a bit of a newbie but have managed to get the code working and I am picking up all expected events when viewed in real time at GA. However, a click on a Paypal Buy Now button that goes to Paypal on the same page as the clicked button does not show up as an event. I will be grateful to know if anyone has solved this or whether the code is not designed to capture redirection events that do not open in a new window. Very useful code.

  • Hi.
    I am a bit of a newbie but have managed to get the code working and I am picking up all expected events when viewed in real time at GA. However, a click on a Paypal Buy Now button that goes to Paypal on the same page as the clicked button does not show up as an event. I will be grateful to know if anyone has solved this or whether the code is not designed to capture redirection events that do not open in a new window. Very useful code. (sorry to George for posting this first by mistake as a reply to your post)

  • Ash

    Using the above code works as expected for file types but external links, mailto, and tel don’t.

    Mailto and tel do not report any events at all. Watching the traffic in the network console it says that HTTP request to Google Analytics was cancelled.

    External links do show up in the Events reporting but oddly they do not appear in Real Time. A marker on the second chart is registered but nothing is displayed in the table below.

    As an extra: what would be the best way to implement a click to register as a Pageview and an Event at the same time? E.g. click a PDF sends an Event as per the blog article code, but it also send /files/name.pdf as a Pageview. I’ve tried this which I think is working but I feel there could be a better way to implement the code:

    ga(‘send’,’event’, elEv.category.toLowerCase(),elEv.action.toLowerCase(),elEv.label.toLowerCase(),elEv.value,{
    ‘nonInteraction’: elEv.non_i ,
    ‘hitCallback’: function() {
    ga(‘send’,’pageview’, elPv.loc,{
    ‘nonInteraction’: elEv.non_i ,

  • Peter Tögel

    I seem to have issues with getting it to work.

    To clarify, …

    Where does the code above go? Inside the head tag or inside the body tag?

    In what order do we need the jquery, the code above and the Google Script Tracking Code?

    Do we need to tag the links somehow or is the script supposed to figure it out?

    Is there a sample HTML page where we can see it working?


  • Nathan P

    Hello guys! Thank you so much for this awesome script. I have been trying to implement it on my organizations website to get the downloads tracking data. So far the script is not working but I believe that has to do with where it is placed in relation to the jquery load. My question is this: Do I need to change my Universal Analytics code at all to enable event tracking for the script to work?

    • Daniël de Wolf

      Hi Nathan,

      UA doesn’t need any change. Be sure to load jquery somewhere in the head. Add the code above just above the body closing tag (). Then it works for me.

      Be sure to check it using the Real-Time / Events page… the Events pages themselves are not real-time, so it takes a while before something shows up there!

  • Daniël de Wolf

    Small improvement for URLs that have querystrings: Replace the $ sign in the filetypes variable (line 3) with [$?]

    This way download links that have querystring variables after the file extension get tracked too!

  • Sheela

    Hi there, I have been trying to implement this code with no success. Our site is upgraded to UA however when I go to Real Time – events to check I don’t see anything. It has been two days and still no success.

  • Gregory Nickoloff

    How would I get and pass the anchor text as labels instead of url?

  • Jason

    Not sure why it’s not working/showing up in GA. I created a basic/blank page with several links (google, yahoo, PDF docs on server) to test, and none of it is showing in GA.

    I’ve got jQuery (1.11.3) in the section. Then right above the ending tag, I’ve got the GA code, then, I’ve got the above code (minus the script tags) as a separate .js file.
    Do I need to wait several days for it to show up in GA? Any help would be great.

  • Is that quote works with magneto?

  • Dave Hope

    Hey, awesome post, thanks! I’d love to get video working in this too. I know I’d need to add another “else if” in but don’t know how to do it?

    So far, I’ve added this “ga(‘send’, ‘event’, ‘video’, ‘play’);” to my GA code but it doesn’t send the Event Label.

    Can someone help me add it to the code above please?

  • Kristin Brinner

    I know this code is from several years ago, but I’m curious if it is still relevant for the way Google’s Universal Analytics works? I have modified the above code for a Drupal website I’m building, and when I follow the code via a debugger, I can see that things appear to be working correctly until I get to the very end (I have changed the name of ‘elEV’ array to ‘anchorElementProperties’):

    ga(‘send’,’event’, anchorElementProperties.category.toLowerCase(),anchorElementProperties.action.toLowerCase(),anchorElementProperties.label.toLowerCase(),anchorElementProperties.value,{
    ‘nonInteraction’: anchorElementProperties.non_i ,

    I get a Uncaught ReferenceError: gaHitCallbackHandler is not defined error, and (unsurprisingly) don’t see any Events being registered when logged into my Google Analytics account. The only parts I have modified is the JS leading up to the if (track) section – after that, it’s all the same code as what is posted in this blog post.

    • Hello Kristin,

      You may have missed lines 67 through 69 – this is where gaHitCallbackHandler is defined -that should get you going!

Ryan Chase
About the Author

Ryan is a Senior Analytics Consultant at Blast Analytics & Marketing. He is passionate about helping businesses improve their usage of data to make better decisions. He has experience across industries and analytics platforms to help set and meet long term business goals.

Connect with Ryan on LinkedIn. Ryan Chase has written on the Web Analytics Blog.

HIPAA and Analytics White Paper CTA

Featured White Paper

Healthcare Analytics and HIPAA: Ways to Minimize Risk and Ensure Compliance

The rise in digital data and analytics adds complexity and risk for healthcare organizations. Those that don’t comply with data privacy requirements, including Health Insurance Portability and Accountability Act (HIPAA), could face heavy fines, civil action lawsuits, and even criminal charges. Not to mention loss of patient trust.

Download the White Paper

Ready To Do More With Your Data?

If you have questions or you’re ready to discuss how Blast can help you EVOLVE your organization, talk to an Analytics Consultant today.

Call 1 (888) 252-7866 or contact us below.