banner



To Locate A Property's Analytics Tracking Code

How the Google Analytics tracking code works

Breaking down that most notorious of isogrammatic JavaScript snippets

A. M. Douglas

If you were setting up Google Analytics right now, you would log into your Google Analytics dashboard, click on the admin tab, select your property and click on tracking code. Once you'd finished that, you would be presented with a screen like this:

The code depicted is as follows:

            (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','YOUR TRACKING ID','auto');
ga('send','pageview');

This is the Universal Analytics Tracking Code, and has been the standard means of implemented Google Analytics since at least 2014. It doesn't look particularly digestible — we've got a function expression wrapping up some assignments and element creation/insertion, and a couple of function calls after that. It's actually very straightforward once you break it down line by line.

So, what does the tracking code actually do?

It doesn't seem like enough to record user interactions in real time, and indeed it is not. The actual analytics code powering all that functionality is about 1400 lines of code when 'pretty printed', though obviously only the fully 'minified ' version is served to the client.

We need to retrieve that code somehow, and you might be thinking that you could just include it in a separate sᴄʀɪᴘᴛ element, above whatever the tracking snippet would be — but that would not be as compatible as the injection method. Injecting the script ensures that it is downloaded and added to the DOM as close to asynchronously as possible in browsers which do not support the 'async' attribute on sᴄʀɪᴘᴛ elements.

So, the analytics tracking code snippet needs to inject the full analytics code into the page, and 90% of what the tracking code snippet does is just that. If we break that snippet up a little bit, you can see that an element is created, set to asynchronously load, given an external source address and inserted into the document — though it might not be immediately clear how it does so without assigning any variables with ᴠᴀʀ statements.

            (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();

/*
Below, the tracking code injects
the full analytics script

*/

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', 'YOUR TRACKING ID', 'auto');
ga('send', 'pageview');

At the same time, you've probably noticed that all the variables that are required seem to be present at the end of the snippet, parenthesised after the main function expression…

To IIFE, or not to IIFE

It's important to note here that this isn't just a function expression, it's an Iᴍᴍᴇᴅɪᴀᴛᴇʟʏ-Iɴᴠᴏᴋᴇᴅ Fᴜɴᴄᴛɪᴏɴ Exᴘʀᴇssɪᴏɴ, or IIFE for short. What that means is that all of the code within the function expression will be executed the moment the browser reaches it when parsing the HTML of the web page.

If you're unfamiliar with IIFEs, you might be wondering why that would be necessary. Well, it's primarily to avoid any conflicts with other JavaScript code you might have on your web page. Within this function expression, variables are 'private', i.e. they cannot be accessed from outside unless they are attached to the global object, 'window'. ∗

∗ NB: an IIFE is not — at least, not purely by virtue of being an IIFE — a 'closure'. The concepts are frequently mixed up, because, technically, in such cases as this, the IIFE does have privileged access to the global object 'window' and yet keeps its variables encapsulated and protected from conflicts/accidental reassignments by other JavaScript libraries/snippets, and thus the IIFE does form a closure — but neither because it was immediately-invoked nor because it is a function expression.

The term 'closure' refers to a function which has privileged access to the local variables of its parent function's context — i.e. where function within a function copies the variables to which its parent has access — whilst its local variables are inaccessible to the parent function. We refer to those variables as having been closed over, hence the term 'closure'. Closures are used extensively in web development, often without developers realising it, and are actually just as frequently created in function statements as they are in function expressions.

The second benefit to using an IIFE in this way is that you can insert variables in the end parentheses and to pass them in as parameters in the function expression. 'i, s, o, g, r, a, m' is probably beginning to look less pointless now. Each letter corresponds sequentially to an argument passed in through the end parentheses, thus:

  • i = window
  • s = document
  • o = 'script'
  • g = '//www.google-analytics.com/analytics.js'
  • r = 'ga'
  • a = undefined
  • m = undefined

So the parameters: 'i, s, o, g, r, a, m' automatically give you the variables you need for the tracking snippet to work, plus two unassigned variables that you can give values when your code executes. Let's rewrite the snippet with traditional variable assignment so you can see what I mean:

            (function () {
var i = window;
var s = document;
var o = 'script';
var g = '//www.google-analytics.com/analytics.js';
var r = 'ga';
var a;
var 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);
})();

ga('create', 'YOUR TRACKING ID', 'auto');
ga('send', 'pageview');

which is basically the same as writing:

            (function () {
var _new_script = document.createElement('script');
var _first_script = document.getElementsByTagName('script')[0];
window.GoogleAnalyticsObject = 'ga'; window.ga = window.ga || function () {
(window.ga.q = window.ga.q || []).push(arguments);
};
window.ga.l = 1 * new Date();

_new_script.async = 1;
_new_script.src = '//www.google-analytics.com/analytics.js';
_first_script.parentNode.insertBefore(_new_script, _first_script);

})(); window.ga('create', 'YOUR TRACKING ID', 'auto');
window.ga('send', 'pageview');

We don't need to dwell on the 'GoogleAnalyticsObject = 'ga', window.ga = window.ga || function…' part, because this code simply records the two windows.ga function calls and their parameters (including your tracking ID and what needs to be recorded, e.g. a pageview) in case the full analytics library takes a little while to download.

You can see that the last two lines of the snippet, which are outside of the IIFE, pass a set of arguments to the global 'ga' function, which the full analytics library will check if it arrives after a pageview should technically have been recorded, and it will then asynchronously send the data to Google.

Behind the bizarre date assignment

For the record, 'window.ga.q' corresponds to the data that should be recorded and sent to your analytics dashboard, i.e. a pageview, in an array that will work like a queue. 'window.ga.l' corresponds to the time at which the event is taking place, hence why it is set to equal '1 * new Date()' — i.e. the current date, as a UNIX timestamp, in milliseconds.

At this point, if you've some experience with JavaScript, you might be wondering why Google has chosen to write '1 * new Date()' instead of 'new Date().getTime()' or even 'Date.now()', both of which would return the same result. There are actually two separate reasons why the latter two options are not used:

  • 'new Date().getTime()' is 20 characters, whereas '1 * new Date()' is 14 characters, so technically there's less JavaScript to download and parse. On a 2G network, that might go some way to making a significant difference.
  • 'Date.now()' is only 10 characters, but it is not supported in older versions of Internet Explorer, so it's a no-go.

If you're wondering how '1 * new Date()' works when, should you try to simply use 'new Date()' you'll get a great big date object which on its own prints a great big string like 'Wed May 24 2017 23:35:20 GMT+0100 (BST)', it works because multiplying that Date object by one automatically casts it to the number type, yielding 1495665320650. Technically, you could trim down the code even more by writing:

            window.ga.l = +new Date();          

because the '+' operator achieves the same effect of casting the Date object to numeric value. You could equally subtract zero, divide by one, or use the bitwise not ('~~') operator — if you really wanted to. †

Warning: neither parseInt (regardless of whether you specify the base or not) nor parseFloat will work, they will both yield NaN (not-a-number). Interestingly, Number(new Date()) does work…

The point is that every decision about the analytics snippet has been taken to keep the amount of code down to an absolute minimum without sacrificing compatibility with older browsers or performance. There is, however, one optimisation I would make to the tracking snippet, and I do so on my own website.

[Aside] How to obsess over performance

Let's look at this part again:

            window.ga = window.ga || function () {
(window.ga.q = window.ga.q || []).push(arguments);
};

The ᴀʀɢᴜᴍᴇɴᴛs keyword refers to an array-like object of all parameters passed to a function — regardless of how many there are. This makes it a powerful keyword, but it comes with a significant cost to performance; the ᴀʀɢᴜᴍᴇɴᴛs keyword prevents performance optimisations being made by Google Chrome's V8 JavaScript just-in-time compiler at run-time — and you'd be surprised at just how much good that JIT compiler does for you.

It is known, however, that accessing ᴀʀɢᴜᴍᴇɴᴛs with an index and accessing the length property of ᴀʀɢᴜᴍᴇɴᴛs is allowed without preventing V8 from making its optimisations.

Thus, while it requires more code, performance can be improved by iterating over the ᴀʀɢᴜᴍᴇɴᴛs object like so:

            window.ga = window.ga || function () {
for (var p = 0; p < arguments.length; ++p) {
(window.ga.q = window.ga.q || []).push(arguments[p]);
}
};

Head or body?

Persnickety optimisations aside, the tracking snippet is very good at what it does: to prime the full analytics library and to retrieve that library by:

  • creating a script element
  • finding the first script element already in the HTML
  • giving the new script element the correct source URL to retrieve the full analytics library
  • setting the new script to be downloaded asynchronously by the browser
  • inserting it before the first JavaScript element on the page ‡

‡ In most cases, this will attach the script to the ʜᴇᴀᴅ element, which is where Google advises the tracking code be placed.

There are differing schools of thought on that approach — some (myself included) think it is not strictly accurate to record a pageview before the HTML document has finished parsing, and that it's easier to detect problems with pages being loaded quickly if the analytics tracking snippet is executed in the ʙᴏᴅʏ element, after everything else has been parsed.

This is more likely to give an accurate picture of how many visitors are truly viewing the page. This approach is more likely to result in slightly longer page viewing times and slightly fewer pageviews, but this isn't gaming the system — it should just be closer to the reality.

Addendum

The snippet I use on my website looks like this:

            (function (win, doc) {
var _script = doc.createElement('script');
win.GoogleAnalyticsObject = 'ga'; win.ga = win.ga || function () {
for (var p = 0; p < arguments.length; ++p) {
(win.ga.q = win.ga.q || []).push(arguments[p]);
}
};
win.ga.l = 1 * new Date(); _script.async = true;
_script.src = '//www.google-analytics.com/analytics.js';
doc.body.appendChild(_script); win.ga('create', 'YOUR TRACKING ID', 'auto');
win.ga('send', 'pageview');
})(window, document);

To Locate A Property's Analytics Tracking Code

Source: https://medium.com/seo-scribe-meta-blog/how-the-google-analytics-tracking-code-works-e0e490e2a97c

Posted by: moorenetaid.blogspot.com

0 Response to "To Locate A Property's Analytics Tracking Code"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel