Optimizations in the Google Analytics tracking snippet

This is not an instructional post on how to use Google Analytics, but a quick teardown to highlight some clever things the script does to optimize page load speed and work around network latency issues.

How does the Google Analytics tracking snippet work? The code they tell you to paste into your site is an opaque mess:

1
2
3
4
(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');

Lets clean up the function contents a little bit.

1
2
3
4
5
6
7
8
9
10
11
window['GoogleAnalyticsObject'] = 'ga';
window['ga'] = window['ga'] || function(){
(window['ga'].q = window['ga'].q || []).push(arguments)
},window['ga'].l = 1*new Date();
a=document.createElement('script'),
m=document.getElementsByTagName('script')[0];
a.async=1;
a.src='analytics.js';
m.parentNode.insertBefore(a,m)

Things to point out:

  • e immediate function is used as a minifier/scope protector. It neatly avoids polluting the global scope and removes the necessity of referring to 'script', window, and documenent. a and m are passed in as declared parameters so the script can save 4 bytes on a var declaration.
  • It dynamically adds a script tag to asynchronously load the full tracking payload, allowing the browser to continue rendering and downloading other assets.
  • The parameter names are not semantic and are chosen to spell out ISOGRAM, which must be an inside joke to Google’s developers.

The async load presents a problem: what happens if the analytics.js file is slow to load? We don’t want any tracking events to be lost, especially since the first calls to the analytics code set up important parameters like the websites owner’s identification.

They neatly solve this by declaring the tracking function ga immediately. It looks to see if there is a window.ga.q object (think queue), and if not, it sets it to a new Array. The ga function can then push events into the queue at will using the usual Array.prototype.push function.

Eventually the analytics.js file will load and empty the queue, replacing the window.ga.q array with its own custom object. That object implements a push function as well, which sends out the tracking beacon on demand.

The power and flexibility of javascript’s dynamic nature gives you a lot of room to find creative solutions.