The Container Snippet: GTM Secrets Revealed

Deciphering the Google Tag Manager JavaScript container snippet. This guide will show you row-by-row what the container snippet does.

First of all, I’m sorry for the wacky title. Sometimes I just want to amuse myself. Nevertheless, this post is about the Google Tag Manager container snippet. There’s nothing secretive about it, but I’m betting many people have no clue what the snippet really does. That’s the revelatory part.

If you’ve never wondered what the snippet does, then shame on you! Remember, you own your page template. It’s yours. Any code that you write there is your responsibility. You wouldn’t let a complete stranger come to your house and paint your walls without permission, would you? So why copy-paste some code that you have no idea what it does? Because Google told you to?

Well, you can trust Google (stop laughing!). There’s nothing nefarious about the container snippet. Regardless, I wrote this post to tell you what it does, so that you can sleep better at night. Or you might learn something new about JavaScript (never a bad thing).

Note that this text should be understandable to JavaScript novices as well, but if you feel daunted at any time, you can just skip ahead to the last chapter. Within is a short recap of what the container snippet does.

So sit back (but lean forward), and enjoy another one of life’s great mysteries unravelled.

The container snippet

You should know what the container snippet is by now. It’s the piece of code that Google Tag Manager requires that you add to your page template, right after the opening <body> tag. You know, this (from my site):

(I left out the <noscript> part, because this post is just about the JavaScript snippet.)

Let’s face it, the snippet code looks gibberish. This is because the JavaScript (that’s right, it’s JavaScript) is minified. Minification is the process of truncating the script to the smallest possible size without losing any data. This means removing whitespace (spaces and line breaks), and reducing variable and function names to single- or double-character length. Minification is done because every single character adds to the page size, and thus to the page load time. The benefit with something as small as the container snippet is negligible, but it is still observed as a best practice.

Because I want this post to add some value, allow me to perform a little makeover to the snippet.

<script type="text/javascript">
(function (w, d, s, l, i) {
    w[l] = w[l] || [];
    w[l].push({
        'gtm.start': new Date().getTime(),
        event: 'gtm.js'
    });
    var f = d.getElementsByTagName(s)[0],
        j = d.createElement(s),
        dl = l != 'dataLayer' ? '&l=' + l : '';
    j.async = true;
    j.src =
        '//www.googletagmanager.com/gtm.js?id=' + i + dl;
    f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-P8XR');
</script>

That’s better! It’s the same code but with some basic formatting. Much easier to read and understand, right?

Positioning the container snippet

You might have wondered why Google so forcefully recommends that you place the container snippet just after the opening <body> tag. There are two good reasons to do so.

First, if you add the <noscript/> tag as well (as you should), this should always be in the body of the document. The tag (which is shown for browsers without JavaScript enabled) contains an iFrame which loads the GTM library. If you add the <noscript/> tag into the head of your document, it can perform pretty wildly with some browsers. You could experiment with leaving the <noscript/> in the body, and placing the JavaScript in the , but I haven’t tested it and certainly don’t recommend it.

The second reason is simple: to maximize data collection, the snippet should be the first thing loaded when the body of the page is rendered. Because the library is loaded asynchronously, there’s no reason to delay it at all. So have it load as the first thing when the body is rendered, so that you don’t risk losing any data due to sluggish DOM elements delaying the page load.

Lines 2 and 15

We can skip lines 1 and 16, since they’re just the HTML SCRIPT tag, wrapping the JavaScript block.

(function (w, d, s, l, i) {
...
})(window, document, 'script', 'dataLayer', 'GTM-P8XR');

To understand the first line, we have to look at the last line as well. This is an example of a self-invoking / self-executing anonymous function, or immediately-invoked function expression. There’s some debate on the semantics of which term you should choose, but for this purpose it’s just that: semantics.

Basically, line 2 of the snippet declares an anonymous function (it has no name) with five parameters. These parameters are served via the function call at the end of the function block, which has, as it should, five arguments.

The whole idea behind an immediately-invoked function expression is to call the function as soon as it has been declared. That’s what lines 2 and 15 of the snippet are about. Line 2 declares a function which is called in line 15, as soon as the function code has been parsed by your browser.

The arguments that are sent with the function call, and turned into parameters in the declaration (the parameters are in parentheses), are as follows:

  • window (w) - the window object represents the open window in the user’s browser and everything in it (the document object model, or DOM, for example)

  • document (d) - the document object is the top node in the DOM tree; it contains the entire HTML document

  • ‘script’ (s) - this string is used to load the gtm.js library (more on this later)

  • ‘dataLayer’ (l) - this string is used to give a name to the dataLayer object; you can rename the object used by GTM by changing this value

  • ‘GTM-P8XR’ (i) - this is your container ID, which you’ll get from Google Tag Manager

Line 3

w[l] = w[l] || [];

If you remember what the parameters and arguments were, this line translates to:

window['dataLayer'] = window['dataLayer'] || [];

Here, the function looks for an object named dataLayer (or something else if you renamed it in the function arguments) in the window object, which contains all the objects on the page. If it finds one, it leaves it be. If it doesn’t find one, it declares it as an empty array.

This is important, because sometimes you’ll see this explicit declaration of the dataLayer object in the page template before the container snippet:

<script type="text/javascript">
    dataLayer = [];
</script>

Explicit declaration of dataLayer is useful if you need some dataLayer variables to be available at the earliest possible moment in your GTM tags. For example, if your page view tag requires some dataLayer variable to exist (for a custom dimension, perhaps), it’s better to declare it in the page template before the container snippet:

<script>
    dataLayer = [{'author': 'Simo Ahava'}];
</script>

This explicit declaration is what the code on line 3 of the container snippet is looking for. If the explicit declaration is found, it is left as it is. If it hasn’t been declared, the container snippet creates the dataLayer object for you.

This brings me to an important point.

If you do not declare the dataLayer object explicitly, do NOT declare it in your GTM tags or after the container snippet. The container creates the dataLayer array for you, and if you try to declare it later on, you’ll end up erasing the original dataLayer, created and used by GTM. After dataLayer has been created, your only interaction with it should be by using the push() method of the array (dataLayer.push({‘property’: ‘value’});).

Lines 4 through 7

w[l].push({
    'gtm.start': new Date().getTime(),
    event: 'gtm.js'
});

This is a very important part of the container snippet functionality.

When the code reaches this point, a dataLayer.push() call is done. Within the curly brackets is an object literal. Object literals are JavaScript objects, which contain any number of key-value (or property-value or variable-value) pairs, often in JSON (JavaScript Object Notation). These objects are wrapped in curly brackets, and pushed to the end of the dataLayer array.

If most of the previous paragraph is gibberish to you, I don’t blame you.

To put it simply, with a dataLayer.push(), you’re pushing an object with properties to the end of the dataLayer queue. These properties can then be accessed in GTM.

This first push, on lines 4 through 7 of the container snippet, contains an object with the following properties:

  • Key 1: ‘gtm.start’ - Value 1: New Date.getTime()

  • Key 2: event - Value 2: ‘gtm.js’

So if you’ve ever wondered when tags with {{event}} equals gtm.js or {{url}} matches regex .* get fired, it’s at this point.

The gtm.start property receives the current time (in milliseconds since Jan 1, 1970) as its value. Brian Kuhn of Google explained that this is used for calculating gtm.js load time and the cache hit rate of the request, so don’t worry about it.

Lines 8 through 10

var f = d.getElementsByTagName(s)[0],
    j = d.createElement(s),
    dl = l != 'dataLayer' ? '&ampamp;l=' + l : '';

Let’s open this up again, using the parameters and arguments we’re already familiar with:

var f = document.getElementsByTagName('script')[0],
    j = document.createElement('script'),
    dl = 'dataLayer' != 'dataLayer' ? '&ampamp;l=' + 'newDataLayer' : '';

The first line stores the first SCRIPT element on the page in variable f.

The second line creates a new SCRIPT element, and stores it in variable j.

The third line does one of two possible things. If you haven’t touched the default name of the dataLayer object (i.e. ‘dataLayer’), just an empty string is stored in variable dl. However, if you’ve chosen to rename the dataLayer (e.g. ‘newDataLayer’), the string ‘&ampamp;l=newDataLayer’ is stored in variable dl. &ampamp; is the same as the ampersand (&), but it has been encoded because some platforms can’t process non-encoded ASCII characters.

These variables will be used in the next lines.

Lines 11 through 13

j.async = true;
j.src =
    '//www.googletagmanager.com/gtm.js?id=' + i + dl;

And let’s beautify this one as well:

j.async = true;
j.src = '//www.googletagmanager.com/gtm.js?id=GTM-P8XR';

Remember, in the last chapter you just created a new SCRIPT element and stored it in variable j? In these lines you add some attributes to it. The container ID is retrieved from function parameter i, and it was originally passed as an argument in the self-invoking function call (you remember this!). Go look if you don’t believe me.

Note that the last line might look different if you chose to rename your dataLayer. In that case, it would actually be the equivalent of this:

j.src = '//www.googletagmanager.com/gtm.js?id=GTM-P8XR&l=newDataLayer';

This means that if you renamed your dataLayer object, this new name is passed as the value of query parameter l with the request to load the gtm.js library from GTM servers.

Anyway, the whole point of these lines is to make the SCRIPT element you stored into variable j look something like this:

<script type="text/javascript" async src="//www.googletagmanager.com/gtm.js?id=GTM-P8XR"></script>

So basically you’ve just created a SCRIPT tag which loads the external gtm.js library with your container ID (and your renamed dataLayer, if you did so) as query parameters.

Line 14

f.parentNode.insertBefore(j, f);

Remember a few chapters back when variable f was defined as the first SCRIPT tag of the document? This line does the following:

  • Look for the first SCRIPT tag on the page

  • Just before that, insert the new SCRIPT tag which loads gtm.js (from the previous chapter)

So the code on line 14 of the container snippet makes sure that the GTM loader is the first SCRIPT tag on the page.

And there you go! Read ahead for a short summary of what just happened.

Conclusions

Here’s a short recap of what the container snippet does:

  1. Declares an anonymous function with five parameters (the name of the dataLayer object and your container ID, among others)

  2. Creates the dataLayer array, if you didn’t do so explicitly before the container snippet

  3. Pushes the first event, gtm.js, into dataLayer, along with the time the push was done

  4. Creates a new SCRIPT tag, which loads the external gtm.js JavaScript library with your container ID (and custom dataLayer object name) as parameters

  5. Adds this new SCRIPT to the page template, so that it’s the first SCRIPT tag on the page

  6. After the function is declared, the function is automatically executed

In the end, it’s a pretty simple piece of code. All it actually does is create (or verify) the dataLayer object, push the first event (named ‘gtm.js’) into dataLayer, and load the external gtm.js library which processes your container.

And that’s the story of the container snippet.

Next up: reverse engineering the entire gtm.js library (JUST KIDDING!).