Tracking Code and Click Response Delays

Sometime last year I tried out Clicky, a web analytics service offering real-time1 traffic monitoring. I was really impressed with a few of the features, in particular the “Spy” feature, which provided real-time data of visitor activity at the user level. Being able to watch, in real time, a user move through a site was a novel thing (although not necessarily a useful thing). With their iPhone-friendly dashboard I could even watch activity on a site when I was out to dinner, which I thought was very cool. (My girlfriend, by the way, did NOT think this was very cool.)

All was good. Monitoring web traffic in real time on the go, it felt like the future had arrived. Surely the jetpacks and hoverboards were just around the corner.

Enter the other shoe. After a few weeks I noticed that something on my site felt sluggish. The something turned out to be clicks on external links, and the culprit turned out to be Clicky.

Clicky’s script was capturing click events on external links and imposing a 500ms delay to allow the script enough time to fire the event data back to their servers. Even though it only applied to external links, a half second delay was too much, so I tweeted the discovery and decided to jettison the script—partly because of the performance hit and partly because the free trial providing access to the Spy feature had expired.

Analytics on the rocks, ain’t no big surprise. We go on. We must go on, we can’t go on, we’ll go on.

Skip ahead to yesterday, when I was asked to add tracking code for Nielsen NetRatings to a site.  It was my first time seeing the code (I’d actually never heard of NetRatings) and I was naturally a little suspicious.  After adding the code I immediately noticed a performance problem: the response to clicking on any link, including internal links (links to pages on the same domain), was noticeably delayed.

A quick look through the code turned up this pause function:

function _rsPause(_rsMillis) {
  var _rsDate = new Date();
  var _rsCurrDate;
 
  do { 
    _rsCurrDate = new Date(); 
  } while(_rsCurrDate - _rsDate < _rsMillis);
}

The default, hard-coded value for _rsMillis was 500, as in 500 milliseconds, as in a half second, as in a really long time by web performance standards.

The important thing to note here is that unlike Clicky’s pause function, which ran only on external links, the NetRatings pause function runs on every link, meaning users are subjected to a half-second delay each time they view a new page on your site.

You may think that a 500ms delay is not that noticeable. If so, I invite you to try out this demo to witness just how noticeable it is. You can also click around on Nielsen Wire to observe the effect on a real site.

At the risk of emphasizing the obvious, I’d like to point something out. As people-who-care-about-performance we spend a lot of time and energy optimizing what happens after the moment the server receives a request. But how we get to that moment is just as important. To the user there is no difference. A half-second wait on the way out is the same as a half-second wait on the way back. It’s just time spent waiting for what they asked for.

Is there anything to be done?

Aside from choosing not to install tracking solutions that come with such a performance penalty (which is not always an option), your best option is to replace the default 500ms timeout in these scripts with something less obtrusive. 100ms is considered the threshold for what we perceive as instantaneous response (see the demo)2. Both Clicky and NetRatings allow you to override the default delay in the initialization code.3 Depending on the script, 100ms may not be enough time to execute the tracking request, so you’ll have to test it out and possibly be OK with not recording every link click.

Final note: I’ve only mentioned Clicky and NetRatings but there are others that employ the same technique. Of course it’s always a good idea to take a critical look at performance effects whenever adding 3rd party Javascript to one of your sites. (Interestingly, Google Analytics seems to be able to capture outbound link clicks [via event tracking] without enforcing a timeout [at least not one that I’ve observed]. But I haven’t done thorough testing. It could very well be leaking lots of events to race conditions.)

Footnotes
  • 1 In this context, let’s assume that real-time = realish-time.
  • 2 See Jakob Nielsen on response times.
  • 3 You can find documentation for the former here.
Both comments and pings are currently closed.

Discussion

Whoa, why wouldn’t NetRatings use a setTimeout for the wait? As written, that code sits and spins on an entire CPU core for half a second!

Dave,
Wow, I didn’t even notice the CPU spike. That is flagrant!

I too wondered why they didn’t use setTimeout. Maybe it was a hack to get around an execution order problem..

Everything about this blog post is awesome: Details about Clicky and NetRatings. Links to a demo. Suggested workaround. I might also point out that there are some HTML5 features that might help the situation, namely A PING.

The while loop actually gives the app time to send back a tracking signal to the server, a setTimeout will be ignored if the user advances to another server.

For Speedo ( https://speedo.no.de ) I went with a different approach than having the “wait” before going to another page. Realtime websocket connections really help here. Also on internal pages, I just fire “sync” event.

Not that my code optimal but there more one way to tackle this issue :)

A very interesting post – ascertaining the delays that Web Analytics tracking code adds to a page is a big concern for many of our clients. But perhaps only a dozen out of our 2800 customers really understand this well enough to talk to us about it. Most don’t know that a problem exists, or how to optimize their tracking code. Way to go on evangelizing the performance impact of Web Analytics tracking code.

Really awesome content!

This certainly put a light on how important is to be sure about how an external script will work in your site.

Thanks for sharing with us this finding.

@Steve
Thanks, that means a lot. A PING looks like the ideal solution. WebSockets also comes to mind as something that could mitigate the problem until A PING is implemented.

@Arnout
setTimeout works too if you prevent the event default action. =) Using WebSockets is a great idea but what are you doing for all browsers that don’t support it?

Great post Rob!

Would be awesome if you share the setTimeout code that ‘works too if you prevent the event default action’.

@Aaron
Thanks! Google Analytics has an example of the setTimeout code on this page about tracking outbound links.

Edit: Also, simple example here: https://jsfiddle.net/LJmQh/