JavaScript 101 For GTM: Part 1
Here’s the link to part 2 of this JavaScript guide.
The thing about Google Tag Manager, or any JavaScript tag manager for that matter, is that there’s JavaScript involved. In fact, the tool itself is just a JavaScript library with some additional bells and whistles (such as a management UI). This means that to make the best of it, some knowledge of JavaScript is warranted, and that’s the point of this post.
This will not be a how-to or JavaScript for dummies, but rather a description of how JavaScript should be managed within GTM. However, I have added some of my favorite JavaScript learning resources to the mix, if you want to start from the beginning. JavaScript is a pretty easy programming language to learn, partly because it’s been “dumbed down” and standardized so much over the years. The biggest difficulty with JavaScript, in my opinion, has nothing to do with the language itself. The biggest problem, by far, is how JavaScript is interpreted by the browsers out there. At its best, the same piece of code works perfectly across all browsers in existence. At its worst, you’ll have to add complex cross-browser fallbacks to make sure your code runs even on certain modern browsers.
The good thing about using GTM, or any library, is that the library itself usually takes care of cross-browser concerns for you. With GTM, however, this is limited to the tag and macro templates that you use. When you start adding custom JavaScript yourself, it’s up to you to be wary of cross-browser support for your scripts.
Quirksmode.org is a pretty awesome resource if you want to find out more about browser compatibility.
Without further ado, let’s go the guide:
-
Favorite resources
-
JavaScript in Google Tag Manager
-
The dataLayer structure
-
Interacting with dataLayer
-
Custom HTML tag
-
Custom JavaScript macro
This is, again, just the first of a two-part series. I’m sorry, I’m just lazy. The sun is finally shining here, I’m looking at the garden, and for some reason the notion of outdoors, smell of freshly cut grass and the sweet liberation only a can of cold beer can provide are edging their way to the top of the “things I’d rather be doing than blogging” list, which is a very, VERY short list indeed.
XThe Simmer Newsletter
Subscribe to the Simmer newsletter to get the latest news and content from Simo Ahava into your email inbox!
1. Favorite resources
Here are some resources to get you going on your JavaScript learning trail:
WEBSITES:
Codecademy - Interactive learning portal for a number of different programming disciplines. Their JavaScript track is excellent to get you started with the language.
Code School - Another training portal. Their JavaScript track is good as well.
w3schools.com - Better as a reference than as a learning resource, but lots of good content nonetheless.
MDN Web API Interfaces - Reference tool for all the web APIs you’ll ever need.
BOOKS:
Douglas Crockford - JavaScript: The Good Parts - One of the best books on JavaScript best practices out there (intermediate to advanced).
Nicholas Jakas - Professional JavaScript for Web Developers - My favorite JavaScript book. The most complete guide out there.
Marijn Haverbeke - Eloquent JavaScript - Excellent book on JavaScript, with a free digital edition!
BLOGS:
DailyJS - Daily tips on JavaScript.
David Walsh Blog - Lots of stuff on web development, with many in-depth articles on JavaScript.
Joe Zim’s JavaScript Blog - Actionable tips and guides for JavaScript and related frameworks.
These are my favorite resources. If you have more suggestions, let me know via e-mail or in the blog comments.
2. JavaScript in Google Tag Manager
In almost all of my Google Tag Manager posts, I use JavaScript in one way or another to get the task done. That’s because JavaScript is THE language of Google Tag Manager. The library itself is a JavaScript library, and all the bells and whistles of the UI are meant to control the type of JavaScript that the tool injects on the web page.
However, you can’t just go around shooting JS code wherever you like. In the GTM UI, JavaScript can only be added in the context of a script. Currently, there are two places which accept JavaScript code raw and unfiltered: the Custom HTML Tag and the Custom JavaScript Macro. So don’t try to write JavaScript in other tag fields, it won’t work.
So if you want to perform custom JavaScript functions, you will either need to create a Custom HTML Tag which executes some code, or if you want to use JavaScript to access DOM elements, for example, you might want to create a Custom JavaScript macro. We’ll get to these soon, don’t worry.
FURTHER READING:
3. The dataLayer structure
A JavaScript tag management solution is based on code injection. This means that the code it executes on your site is alien to the page template. The only thing the page template originally has is the container snippet. This snippet acts as the invitation for GTM to perform its magic on your page.
Because GTM injects the code it wants to execute, it’s vital that it uses its own data structure. If there was no proprietary data model, all the variables GTM would be accessing / modifying would run the risk of causing conflicts with other libraries. For instance, say you have a global variable pageHeader
created by your developers, and it stores the main header of your page template. Then GTM comes along and uses pageHeader
to manipulate the document title that is pushed to Google Analytics. Because it accesses the same global variable that you use to store on-page information, the risk that GTM would break your page template becomes a horrible reality.
With dataLayer, successful (read: smart) use of GTM is restricted to only those variables that are exposed within the dataLayer structure. Thus the only risk you run is having another library which ALSO uses dataLayer (spelled exactly like that). The better known that the dataLayer standard becomes, the less of a risk this will be, since other libraries will know to avoid using this name.
dataLayer is a JavaScript object of the type Array. An Array is a special type of object. The difference is that in JavaScript, objects have properties, whereas Arrays have members. An Array member can be an object.
Object properties are defined with specific notation, e.g.:
var sampleObject = {'First-name': 'Simo'};
alert(sampleObject['First-name']);
alert(sampleObject.First-name); // Error!
So this is an object, not an Array. If you want to get the terminology straight, in the first line I created an object using object literal notation. That’s the curly bracket thingamajig there. In the second line, I retrieved the ‘First-name’ property of the object by using square bracket notation. You could also use dot notation to access object properties (third line), but with ‘First-name’ that would cause a NaN (Not a Number) error, because the hyphen in the name is interpreted as a minus sign. Thus JavaScript in its infinite wisdom is misguided enough to think that sampleObject.First returns a number of which another number name is subtracted. But never mind, this was just a detour.
The thing to understand about dataLayer is that an Array behaves a bit differently than your run-of-the-mill JavaScript object. First of all, an Array is like a table or queue, however you want to look at it. You can add multiple members to it, and any one of these members can be of any JavaScript type, such as objects.
In GTM, dataLayer is (almost) always interpreted as an Array of objects. Thus you can have the sampleObject I created above as a member of dataLayer:
var dataLayer = [{'First-name': 'Simo'}];
As you can see, it doesn’t matter any more that the object was called ‘sampleObject’, since from this moment on it will only be referenced to using its index number in the Array. Index numbers start from 0, so if this is the first object in the array, it’s reference is dataLayer[0]. The next object that is added to the Array would have index number 1, and so on.
You can access object properties in an Array using the same notation as you just learned of in the beginning of this chapter. However, first you need to know which index the object whose property you want to access resides in.
So, if you’re still following me, to access the ‘First-name’ property of the FIRST dataLayer index, you’d need to use something like this:
alert(dataLayer[0]['First-name']);
This would pop up a dialog with “Simo”. However, in GTM you don’t need to worry about dataLayer index numbers, since you can just use the Data Layer Variable Macro. I’ll have more about interacting with dataLayer in the next chapter.
FURTHER READING:
4. Interacting with dataLayer
In GTM, you basically interact with dataLayer in three different ways:
-
First, you define dataLayer as an Array
-
Next, you push objects and properties into dataLayer
-
Finally, you access these properties using a Data Layer Variable Macro
Let’s go through these step-by-step.
You define dataLayer by declaring the variable and setting its type to Array. The most common way to do this is to use literal notation:
var dataLayer = [];
This tells your page that a new variable called dataLayer has now been declared, and the square brackets define its type as Array. After this, you can start pushing stuff into the structure, or if you have some properties that you already want to push, you can just add them to the declaration:
var dataLayer = [{
'pageTitle': 'Homepage',
'visitorType': 'Loyal customer',
'loggedIn': true
}];
So here I create dataLayer as an Array (that’s the square brackets) with a single object (that’s the curly brackets) with three properties (that’s the ‘key’: ‘value’,) stuff. As you can see, there’s no comma after the last property. Also, true does not have single quotes around it, since I’m storing a value of Boolean type (true/false) and not a string.
The thing about the dataLayer declaration is that it should only ever be done once. You see, every time you redeclare a variable, its previous reference is overwritten! You have to be really careful about this, since if your developers accidentally add the dataLayer declaration twice on your page, its previous properties will be overwritten by the second declaration. That’s why its safest to declare the structure once and then just consistently use push() for all modifications later.
The push()-method is what all Arrays come equipped with. Using this method adds an object to the end of the Array, creating a new index for it automatically. Thus nothing is overwritten. You can have a dozen objects with exactly the same properties in dataLayer, and you’d get no errors. Note that GTM’s Data Layer Variable Macro has a way of navigating around this issue (see below).
To push a new object into dataLayer, the following notation should be used:
dataLayer.push({'loggedIn': false});
This just pushed a new object into dataLayer with a single property: ‘loggedIn’ with the value false. So I can easily have dataLayer with two objects, both with property ‘loggedIn’ being something different. That doesn’t matter, since they’re in separate objects.
Trying to push the same property twice into the SAME object would not result in an error, either. However, only the last property would remain in the object.
The thing about push() is that it won’t work if dataLayer hasn’t been declared as an Array. That’s why in your page template the following is a good way of prefixing the dataLayer.push() call:
window.dataLayer = window.dataLayer || [];
dataLayer.push(...);
The first line checks if dataLayer has already been declared. If it has, all is well, but if it hasn’t, it is then and there declared as an Array. This basically makes it so that the push() method will almost always work. The only way it won’t work is if dataLayer HAS been declared but as something other than an Array (pretty rare situation).
Note that when working in GTM (Custom HTML and Custom JavaScript), you won’t have to add this check to your code, since the GTM container snippet will always declare dataLayer for you if you haven’t done so explicitly in the page template.
Finally, the way that the Data Layer Variable Macro works is that when you give it a property whose value it must return, it starts going through the dataLayer properties one by one, starting from the newest. If it finds a property with the name you gave, it returns its value and stops traversing dataLayer. So if you have two objects in dataLayer with the property ‘loggedIn’, only the most recent property value will be returned by the Data Layer Variable Macro.
In layman’s terms, this means that for Google Tag Manager, pushing an object into dataLayer with a property that already exists in dataLayer overwrites the previous property value with the new one. Of course, it doesn’t really overwrite anything, but the Macro is only interested in the latest value.
There is a quirky exception to this, and that is if you push an Array into dataLayer.
If you then push another Array with the same name, GTM will only ‘overwrite’ those Array indices that are shared by the two Arrays, but if the first Array had more indices, then those will remain for GTM to use. Allow me to demonstrate:
dataLayer.push({'products': ['a', 'b', 'c', 'd']});
...
dataLayer.push({'products': ['e', 'f']});
In the first line, I push an Array with four members (a, b, c, d). A bit later on, I push another Array with the same name, but this time with just two members. If GTM would be consistent, the second time around the Data Layer Variable Macro would have just two members in the ‘products’ Array, but right now GTM works so that it just overwrites the first two indices in the Array. Thus the Macro would actually see ‘products’: [‘e’, ‘f’, ‘c’, ’d’]. This is a bit buggy to me, as to be consistent the second Array push should make it so that the Macro only accesses the values in the second Array.
Phew, this was a lot of information, I hope you’re still with me here.
5. Custom HTML Tag
A Custom HTML Tag allows you to inject HTML markup in the site. This means that if you want to run custom JavaScript in a Custom HTML Tag, you need to create the script context for the code. In HTML, you establish a script context with the <script>
element:
<div id="html_markup_here">This is still just normal HTML markup</div>
<script>
alert("And this is JavaScript!");
</script>
If you try to run JavaScript without the script block, you’ll run into some serious validation errors.
Note that you don’t need the legacy type="text/javascript” attribute in your <script>
tags, unless you’re still using HTML4 (gasp) and you want your code to validate. All modern browsers can do the math and make the logical conclusion that a SCRIPT element with JavaScript code inside should be interpreted as, surprise surprise, JavaScript.
6. Custom JavaScript Macro
In Google Tag Manager, a macro is designed to return a value when it is called. That’s why you have all those cool out-of-the-box macros out there, which each return some amazing piece of information about the site, the page, or the visitor.
If you haven’t already, be sure to read my macro guide, as it sheds more light on this topic.
With a Custom JavaScript Macro, it’s function (pun intended) is also to return a value. If there’s no return statement or if the macro isn’t wrapped in a function (only functions can return values), you’ll run into errors in GTM.
So here are the things to remember:
1. Always wrap your Custom JavaScript macro in an anonymous function.
2. Always return a value (any value) with a Custom JavaScript macro
An anonymous function is a function without a name. Why is this necessary? Well, you don’t create a function in a macro so that you call it on a whim using its name. No, the sole reason for a JS macro’s existence is that it’s called and its value is resolved at the same time, using the {{macro}} syntax of GTM. This is, again, one of the ways GTM ensures that global variables are not being messed about with. If you could name your functions, you might be using a name reserved for some other library. This would potentially have nightmarish results.
And why return a value? Well, to reiterate, a macro’s function is to return a value. When you call a JS macro, it resolves some operation right then and there with all the data available at the moment it was called. It then returns a value based on this operation. What would be the function of using the macro syntax {{macro}} in your fields if not to retrieve a value? Stop asking silly questions.
An example of a proper Custom JavaScript macro:
function() {
return window.location.href.toLowerCase();
}
Here you have an anonymous function declared on the first line, a return statement with the operation on the second line, and the closing of the function block on the third line.
When this macro is called, it returns the URL of the current page transformed into lower case. This is a prototypical GTM Custom JavaScript Macro. It’s just an anonymous function which does not accept parameters. Thus its sole objective is to return a value, based on some operation on some other value that can be found in the document object model (more on this later). Remember that you can access other macros in your Custom JavaScript Macros, so you can do a lot of pretty complex stuff with Custom JavaScript macros. For example, the following macro returns the first folder in the URL path (e.g. “simo” in /simo/articles/analytics):
function() {
return {{url path}}.split("/")[1];
}
A Custom JavaScript Macro is probably the most versatile tool you have available in your GTM toolbox, and you should definitely check out my other Google Tag Manager posts for creative ways to use them.
Conclusions
Phew, that was a lot information there. I’m going to have to split this into a two-parter, just to keep me motivated :)
I hope you take a look at the JavaScript resources I wrote of in the first chapter. Even if you’re well versed in the craft of client-side programming, most of the time its good to refresh your knowledge with some “basic stuff”.
The thing with GTM is that in addition to functioning like your average JavaScript library, it comes along with certain proprietary features that require you to adapt your JavaScript skills somewhat. The purpose of this (and the following article) are just that: make you knowledgeable of what GTM requires from you and the code you create.
Here’s the link to part 2 of this JavaScript guide.