netnix.org
Networking and Unix


Tracking Downloads with Google Analytics

 April 27th, 2014Apr 27th, 2014        3

Google Analytics does a very good job at tracking page views out of the box, but requires a bit more technical expertise to successfully track download events. There are lots of different WordPress plugins available, but they all seem very complicated and none that seemed to track download events out of the box. With this in mind I decided to come up with a simple solution that only requires a single line to be included in your header section for it to track page views and download events.

The below snippet assumes you have migrated to Universal Analytics (analytics.js), but the equivalent code using ‘ga.js’ is just as simple. We start off by creating an external JavaScript file, which I have called ‘my_ga.js’ in this example:

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXXXXX-X', 'mydomain.com');
ga('send', 'pageview');

window.onload = function() {
  var a = document.getElementsByTagName('a');
  for (i = 0; i < a.length; i++) {
    if (a[i].hostname === location.hostname) {
      if (a[i].href.match(/^https?://.+.(zip|dmg|txt|cfg|gz|pl|pdf)$/i)) {
        a[i].setAttribute('target', '_blank');
        a[i].onclick = function() {
          ga('send', 'event', 'Downloads', 'Click', this.getAttribute('href'));
        };
      }
    }
  }
}

You should be familiar with the first part of this excerpt as it is your standard Google Analytics tracking code, but the part which I have added is the 'window.onload()' function definition. Normally to track download events, Google recommends that you fix the following tracking code to each of your anchor links:

<a href="/download.tar.gz" onclick="ga('send', 'event', 'Downloads', 'Click', '/download.tar.gz')">Download</a>

The above code will call the 'ga()' function when you click on the link and track the event before the download commences - this is the same approach that I have used, except I have automated it using JavaScript so you don't need to manually ensure this code is attached to all your links.

The logic in the code is simple and the pseudo code is below:

  • Wait for the page to load so all the anchor links are available.
  • Loop through all the anchor links in the document.
  • If the hostname of the link matches the hostname of the website then the link is local and we should track it - we are measuring locally hosted downloads.
  • If the link matches the case-insensitive regular expression '^https?://.+.(zip|dmg|txt|cfg|gz|pl|pdf)$' then we wish to track it - this matches extensions that I want to be treated as downloads - feel free to change this.
  • We then change the 'target' attribute of the link to '_blank' which causes the link to open in a new tab/window. This is a workaround for a bug on some Webkit browsers (most notably Safari) where it wasn't tracking events. There is an issue where the download commences before the tracking code has fully finished which results in all JavaScript being terminated. By causing the link to open in a new tab/window the tracking code continues to execute in the original window.
  • We then define our 'onClick' event to record the event using Google's standard event tracking code - we use the link's 'href' attribute as our tracking label.

The final step is to ensure you include your external JavaScript file 'my_ga.js' in all your pages and the simple Insert Headers and Footers WordPress plugin is perfect for this task. We configure the plugin to insert the following line in the header section of all our pages:

<script src="/my_ga.js"></script>

You also have the advantage with this simple approach of manually adding the above code to bottom of the 'head' section of any non-WordPress pages to enable tracking.

However, the issue with the above approach is that all tracked links now open in a new tab/window - in most cases this isn't a problem as the web browser will close the tab/window once the download commences. But, we are still changing the user experience because we are changing the 'target' attribute. Unfortunately I can't find a way to ensure we track all links 100% of the time without doing this. A similar approach that Google recommends is to use the 'hitCallback' property to define a callback function that executes once the tracking event has finished - this is used to start the download after the function returns. This ensures the event is always tracked, but if the function doesn't succeed then your download never starts. This approach is documented below for reference which uses a JavaScript IIFE to ensure the local scoped 'url' variable (the passed value of 'a[i].href') is locked in:

      ...
      if (a[i].href.match(/^https?://.+.(zip|dmg|txt|cfg|gz|pl|pdf)$/i)) {

        (function(url) {
          a[i].onclick = function() {
            ga('send', 'event', 'Downloads', 'Click', this.getAttribute('href'), {
              'hitCallback': function() {
                document.location = url;
              }
            });
            return false;
          };
        })(a[i].href);

      }
      ...

Although it doesn't look like it is changing the 'target' attribute of the link, but by using 'document.location' we are indeed ensuring the page always loads in the local window - if the user had actually specified '_blank' as the target to open in a new tab/window it would be ignored. Also, this approach doesn't provide a good user experience on Internet Explorer as it presents the user with the yellow information bar and doesn't seem to present a download prompt.

Other users have suggested an alternative approach of attaching the tracking code to the 'onMouseDown' event instead, as this event fires earlier so the JavaScript tracking code has longer to finish. However, although this increases your tracking success rate, it doesn't guarantee all events are tracked and it also could count false positives if the user changes their mind after half clicking a link.

If anyone has any better suggestions which will work in all cases and on all browsers without changing the user experience then I would be glad to hear them.

General Google Analytics JavaScript


Thoughts on "Tracking Downloads with Google Analytics"...

by senthil on May 13, 2014 at 09:50

i was expecting this a very long time. Thanks for the implementation tip.


by Deb on July 21, 2014 at 17:46

Thanks for writing and publishing this code. Is there a possibility that the use of the external Javascript file could affect page loading times or performance?


by Peter Davidson on June 29, 2015 at 01:37

Hi Chris, Thanks for detailing how to do this. I have written an article on how to do this using Google Tag Manager. It makes good use of GTM variables and shows how flexible it is. You can check it out here:

Track File Downloads in Google Analytics using Google Tag Manager
http://www.seoworks.com/01-seo-news-views/track-file-downloads-in-google-analytics-using-gtm/

It may be an easier solution for some, especially if you have a lot of tags to manage anyway with GTM.