Tracking outbound links? I bet you only measure 70 percent

A lot of site owners want to track outbound links so they can see how often they are clicked. It's also useful to see when and where people left your site. Google Analytics knows the exit time of your last page, where in normal cases the last visited page is not counted in the spend time on site/page.

But, there is a big but. A lot of outbound link tracking is done like this:

[code lang="html"]<a href="" onclick="trackClick(this)">Nice site</a>

And this is what happens when you click on this outbound link:

  1. The onclick is executed first
  2. The trackClick function generates an IMG element with a URL that points to the Web Analytics vendor
  3. The onclick function is handled and the browser starts with the href part

And then the race starts:

  • The tracking pixel is requested from the Web Analytics vendor
  • The browser kills all activity and opens the URL in the href attribute

But we can't tell which one is first. A small delay on the Web Analytics vendor side is deadly for the tracking, often the pixel request is killed by the browser. You can see this in a simple tool that shows you all activity in your browser. Because Yoasts' post is was the trigger to blog this I will use his site as an example.

Take a look at his Clicky outbound link tracking post. It has an outbound link in the first sentence. And this happens when I'm clicking it:

The first two lines are the main Clicky script with all tracking functionality and the basic pageview measurement. The third line is the outbound link tracking: in this case the tracking is send. The line is red because the browser stops waiting for the response, but that isn't necessary for the tracking. Sometimes this line isn't generated and no tracking is done...

A solution

In the post title I mentioned 70%, but that's merely a guess based on what I have seen in cases where I solved this. After implementing a little timeout before the link opens we saw a lot more outgoing links being measured. I already mentioned the solution: a little timeout to give the tracing some time to execute before the link opens.

But there are a few caveats using this technique:

  • Using "" triggers the popup blocker of Internet Explorer 9+. That's because the link is opened with a script in stead of a user initiated click. We have to use location.href to prevent that from happening.

  • Using "location.href" doesn't pass on referrer information in Internet Explorer 6, 7 and 8. External site won't see traffic coming from you.

This is de jQuery code I use right now to track outgoing links:

[code lang="js"]if(typeof jQuery == 'function')
jQuery(function ()
jQuery('a[href^="http"]').click(function (e)
var url = this.toString();
if (url.indexOf(document.domain) == -1)
_gaq.push(['_trackEvent', 'clickouts', jQuery(this).attr('href').replace(/https?:\/\/(.*)/,"$1")]);
var target = jQuery(this).attr('target');
if (target == "") { target = "_self"; }
var a = document.createElement("a");
if ((! || ((jQuery.browser.msie) && (parseInt(jQuery.browser.version) > 8))) {
location.href = url; // for chrome and IE9+, target is lost
} else {
a.setAttribute("href", url);
a.setAttribute("target", target); = "none";
aElm = document.body.appendChild(a);; // for IE6,7,8 to pass on referrer
}, 50); // 50ms should be enough timeout

Is this the best way to track outgoing links? For what I've seen this is one of the best and most reliable approaches. But I'm happy to receive additions that makes it even better.

Ps. In this example I used Google Analytics to track outgoing links, but you can change that part to whatever tracking you want.


Eduardo Cereto proposed to use the mousedown event without an additional timeout. I tested this on several sites and it looks like the most reliable solution right now. The code is much easier also:

[code lang="js"]jQuery(function ()
jQuery('a[href^="http"]').mousedown(function ()
var url = this.toString();
if (url.indexOf(document.domain) == -1)
_gaq.push(['_trackEvent', 'clickouts', jQuery(this).attr('href').replace(/https?:\/\/(.*)/,"$1")]);

Click to activate social bookmarks

  • Eduardo Cereto

    It's a good point and I've studied the subject a lot in the past.

    I used to have a timeout like this. But it turned out that 50ms increased the coverage but still was not 100% effective. So we increased that, and at some point the user started to notice that increase, and started to click multiple times firing the outbound event multiple times. I also never licked the idea of interrupting the original event since it may affect other listeners that may be setup.

    I decided to go for a slightly different approach. Instead of listening for click I listen to mousedown. On mousedown usually happens ~50ms before the click and usually there are no other listeners on this event, so less interference with an automatic timeout.

    The bonus is that it still track if the user right clicks and click on open in a new tab. Which it doesn't if you listen to 'click'.

    I think 70% is a little bit harsh, but I'm sure you lose a couple clicks every now and then. The important point is that you have something that is consistent and represent a good sample of your audience.

    • André

      I like the idea of using the mousedown event. With that you don't need the timeout anymore. Will certainly try that.
      70% is a bit harsh, but on huge sites we saw that increase after deploying the timeout. On smaller site (smaller DOM) it is perhaps more than 70% that's being tracked.

  • Michaël van Oosten

    I haven't tested it, but this might be of interest:

    You should be able to push functions into _gaq. In that case you're absolutely sure measurement is finished.

    • André

      That solution has the same problem as I mentioned in the blogpost: the tracking is ready and the IMG element is added to the DOM. But that doesn't mean the IMG itself is already being called from the Google servers.

  • Ed

    Hi Andrei,

    First of all, great post!

    When using GetClicky, how would the HTML markup look like when tracking links using Eduardo's code? - Using the mouse down event?

    I understand Eduardo's code would then go between the <head tags and the actual html link?

    How would that look like?

    Something like this?

    <a href="">Nice site</a>

    I would like to track which links on a page gets a better CTR when experimenting with positioning and call to action text.

    • André

      As far as I know, Clicky tracks outbound links autmatically? So you don't need to add extra code.

  • Mark van Kasteren

    The jQuery solution works perfectly. Google Analytics advices in their help section to use a 1 second delay for tracking outgoing links with a JavaScript solution:

    • André

      The timeout gave some problems with: popupblockers, people that ctrl-click to open a new tab, safari users that couldn't open links, external sites that don't receive referral info (and therefore don't know who send the traffic), etc. I tested this in many browsers, but I nowadays wouldn't recommend the timeout version, I would use the mousedown code.

  • Madarás Martin

    And what if I use _blank in link and open pages in new window? Will measure be100% objective then?

    • André Scholten

      Absolutely, that's a good solution too.

    • Ram Shengale

      I also think that's a good solution.

  • Leo

    How do you guys use this code to fire and pixel image on click?

  • debruin

    Great info, hope I'll be able to track al outbound links.

  • Joe Anderson

    You can now use the built in hitCallback to deal with this:

    Setting the Hit Callback

    In some cases, like when you track outbound links, you might want to know when the tracker is done sending data. That way you can send a user to their destination only after their click has been reported to Google Analytics. To solve this, the send command allows you to specify a hitCallback function in the field name object that will execute as soon as analytics.js has completed sending data. Here's how to set the hitCallback function:

    ga('send', 'pageview', {
    'page': '/my-new-page',
    'hitCallback': function() {
    alert('analytics.js done sending data');

    In this example, the field name object configures both the page parameter, as well as setting the hitCallback. Once the tracker has completed sending data, an alert box will be shown to the user.