Fun With Google Tag Manager: Part 2

ShareShare on Google+200Tweet about this on Twitter16Share on LinkedIn31Share on Facebook7

Apologies for leaving you hanging. It’s now almost three weeks since I published the first part of this post, and I’m sure you’ve been holding your breath ever since.

There’s been a lot going on since the last post. First, my favorite sports team in the world, San Antonio Spurs, won their fifth NBA championship from the defending champs, Miami Heat. Next, my wife and I moved to our new house, and we’ve been remodeling ever since. It’s been hectic with work and life getting mixed up in a potpourri of saw dust and web analytics, but now it’s time to return back to the wonderful world of Google Tag Manager!

I promised you four new tips in this one, but I’m going to top myself and give you six gems from the world of tag management. Here’s what you’ll get:

  1. dataLayer declaration and push
  2. Using eventCallback in dataLayer.push()
  3. Stopping a Timer Listener
  4. Naming and version control conventions
  5. Fire a single tag multiple times
  6. {{true}} and {{false}}


dataLayer declaration and push

Google Tag Manager uses dataLayer as its own proprietary data model. dataLayer is a JavaScript object of Array type. This means that it has some methods unique to Arrays, such as push().

[See my comment to this post here for an overview of how to access Array objects vs. object properties.]

The thing is, you can’t use dataLayer.push() if you haven’t declared dataLayer as an array first. The repercussions of this are that if you try to use dataLayer.push() before dataLayer has been declared as an Array, or if it’s been declared as something different (such as a normal object or string), you’ll get some errors:
Errors with dataLayer
So here’s the first tip:

You need to declare dataLayer as an Array before you can use its push() method.

To turn dataLayer into an array, you need the following code before the container snippet:

var dataLayer = [];

If you don’t declare it with this explicit statement, then GTM will declare dataLayer for you. This is very important, since it ensures that you will always have dataLayer available for your tags.

The thing with JavaScript objects and primitives (numbers, strings, etc.) is that if you redefine a JavaScript entity, its previous state will be overwritten! JavaScript entities are dynamic, so you can redefine a string as an object, an Array as a number, and so forth without any difficulty. There’s also no way of protecting a global variable such as dataLayer from being redefined as a different type, so precautions must be taken so that you don’t mess things up.

Why is this important? Well, if you have more than one dataLayer declaration on your page template OR in your GTM tags, you will erase all previous information in the structure! Oops. Here’s an example:

var dataLayer = [{'pageCategory': 'home', 'logged-in': 'true'}];
...
var dataLayer = [{'event': 'allDone'}];

After the first line of code, dataLayer has a single object with two properties: pageCategory and logged-in. A little on further down the code is the second declaration. Now, after this code is run, dataLayer will still only have one object, but with just the event property! Can you see how harmful this is? None of the previous information is recoverable after this.

The safest way to prevent this is to preface the ONLY dataLayer declaration on the page with the following line:

window.dataLayer = window.dataLayer || [];

And then only use dataLayer.push() to add objects to the Array. This line ensures that if dataLayer has already been declared, it won’t be overwritten. It’s not foolproof, since it doesn’t test whether the global variable dataLayer is of type Array or not, but I think it’s good enough. If you have another JavaScript library which HAPPENS to use a variable called dataLayer as well, you might be in trouble. But this is what testing and debugging is for.

So here’s my final tip for this section:

Declare dataLayer JUST ONCE and use dataLayer.push() for all interactions afterwards.

In your GTM tags you can just use dataLayer.push(), and you don’t need to check whether dataLayer exists or not, since GTM declares it for you in the container snippet. But when working on the page template, you need to be extra careful not to overwrite the Array.

Back to top

Using eventCallback in dataLayer.push()

You can use the eventCallback dataLayer variable in the push() method to execute code after all dependent tags have fired. I know, that was a mouthful but it’s really simple. Take this example:

dataLayer.push({
  'event': 'fireEvent',
  'eventCallback': function() {
    console.log("Event has fired");
  }
});

Well, it’s a pretty lousy example, but it does what it’s meant to. After all tags that fire upon {{event}} equals fireEvent have completed, the eventCallback function will execute and you should see the text in the JavaScript console.

eventCallback is, in essence, a shorthand for the proprietary hitCallback feature that you can invoke in your Google Analytics tags.

You can use this for a quick callback, such as waiting for all tags to fire before a link redirects or pushing a second event as the callback to impose a rudimentary tag firing order.

Back to top

Stopping a Timer Listener

You know of the Timer Listener, right? You can use it to have an event push every X milliseconds. You can limit the number of times the timer fires, or you can just let it fire from here to eternity.
Timer Listener
The thing is, you might want to halt the listener before the limit is reached. For example, if you’re using the Timer Listener to send pulses reflecting the time the user has spent digesting content, for example, you might want to halt it once the reader reaches the end of the page to keep your events from being cluttered with timer hits.

To halt a timer, you need to use the window.clearInterval() method. It requires a parameter, namely the ID of the timer that is firing.

GTM’s timer listener is your run-of-the-mill JavaScript setInterval() timer, and its ID is stored in, what else, a dataLayer variable called gtm.timerId. So to halt the timer, you need to send the content of gtm.timerId as the parameter of window.clearInterval(). For this to work, you’ll need to create a new Data Layer Variable Macro which refers to gtm.timerId:
Data Layer Timer ID
After this, whenever you want to halt a timer, all you have to do is have the following line of JavaScript in your Custom HTML tag or your Custom JavaScript macro:

window.clearInterval({{timer id}});

This will halt the most recently fired timer. So if you have multiple timers, you might have to edit the code somewhat to find the proper timer (easiest way to do this is to use {{event}} equals your_timer_event as one of the rule conditions of the tag that stops the timer).

Back to top

Naming and version control conventions

GTM doesn’t exactly shine when it comes to managing a bucketload of tags, macros, and rules (TMRs). Because of this, it’s really important to adopt a good naming convention so that you can make sense of what you have in your container.

[For alternative approaches (more explicit naming to satisfy the in-tool search), check Doug Hall's original, excellent post on online-behavior. A more recent post by Justin Goodman covers similar ground.]

I use a very simple naming convention in all my TMRs. It scales pretty nicely, as it works in large enterprise implementations as well as in smaller containers. Also, the most important thing is that it’s readable.

Here’s what I use.

TAGS
Tag type – purpose
HTML – Weather API Call
HTML – Social Share Tracking
UA – Page View Tracking
Listener – Link Click
Listener – Click

Having the tag type is pretty redundant since you can see that in the second column of the tag list. However, I enjoy that it makes the list look a bit less cluttered.
GTM Naming Conventions
RULES
Key macro – trigger
Event – gtm.js
Event – Outbound Link Click
URL – Thank you page

The key macro is what drives the rule. Most often its an {{event}}, but every now and then you might have the same event across many rules, and then you’ll need to use some other macro to express the difference between the two rules. An example of this is the URL rule above. It’s underlying {{event}} is gtm.js, so I have to use the URL condition to make it different from all the other gtm.js rules.

MACROS
What is returned
Random Number
Debug Mode Status
Element URL
URL Hostname

With macros, we’re only interested in what they return, since that’s what macros are used for. So in the macro name, it’s important to express just what is returned when the macro is called.

As for version control, my tip is this:

Name your versions AND add notes!
Name your versions and add notes
That’s it. It’s so important to annotate your container versions with information what was updated and what changed. This is the only way to stay on top of your game, especially if you’re dealing with a huge version history. Be pedantic about this and make sure it’s part of your quality assurance process for tag management!

Back to top

Fire a single tag multiple times

Here’s a common problem: You have multiple trackers firing on the same domain, but you want to use just one tag for them. Well, currently GTM doesn’t have a switch with which you can fire the same tag multiple times with different parameters, so you’d need to create a separate tag for each tracker.

However, there’s a workaround. You can use hitCallback, the Lookup Table Macro and a global JavaScript variable to fire your tag multiple times with different values in the fields! Here’s what you need:

JAVASCRIPT VARIABLE MACRO {{tag callback counter}}:
JavaScript variable macro
LOOKUP TABLE MACRO {{tracker name}}:
Lookup Table Tracker Name
HITCALLBACK MACRO {{tag callback function}}:
Custom JavaScript callback
So, before we check how the tag is set up, let’s go over these macros. The {{tag callback counter}} is a JavaScript variable macro which refers to the global JS variable you set up later in {{tag callback function}}. The idea here is that the {{tracker name}} Lookup Table macro returns a different string, depending on what the counter is. Every time the callback is executed, the counter goes up by 1.

The key is to use your first tracker as the default value of the Lookup Table macro. This is because when the tag first fires (on {{event}} equals gtm.js), the JavaScript variable that {{tag callback counter}} refers to will not exist yet (since it’s defined in the CALLBACK of this tag).

When the callback is executed, a GTM event is pushed into dataLayer and the counter value is increased by one. You define the maximum number of times the tag should fire with the maxRepeat variable in the callback function. Just remember to have a row for each iteration in the Lookup Table, otherwise the tracker in the Default Value field of the Lookup Table will be returned for each iteration that doesn’t have a row in the table!

In your tag, add the following in the Fields to Set:
hitCallback field in tag
This will ensure that the callback function is executed every time the tag has fired.

Now that you have your Lookup Table, you can use it in the Tracker Name field of your tag:
Tracker Name
Using the macro here makes it so that with every iteration of the tag, a different value is set as the Tracker Name, thanks to the Lookup Table and the counter that increases by 1 with every pass.

Finally, you need to add TWO rules to the tag. That’s two rules, not two conditions. The rules are

{{event}} equals gtm.js

and

{{event}} equals tagCallback
Two firing rules for the tag
Having two rules means that the tag will fire when and if either one is matched. So first it will fire upon {{event}} equals gtm.js, since that’s the event that is pushed when GTM is first loaded. Next, it will fire every time {{event}} equals tagCallback, so twice in my example. Thus, the tag fires three times with three different tracker names.

You could use this also if you have different properties you want to track to. Just replace the tracker names with UA-XXXXX-Y codes and you’re set!

Here’s a quick recap of what happens:

  1. Tag is fired upon {{event}} equals gtm.js. Since {{tag callback counter}} can’t find the global variable, the Lookup Table macro {{tracker name}} returns the default value, which is localTrackerA
  2. When the tag has fired, the counter is created with value 0, and the tagCallback event is pushed into dataLayer
  3. tagCallback triggers the tag again, and this time the Lookup Table finds the counter with value 0, and returns localTrackerB
  4. When the tag has fired, the counter number is increased to 1, and the tagCallback event is pushed into dataLayer
  5. tagCallback triggers the tag for the third time, and again the Lookup Table finds the counter with value 1, thus rollupTracker is returned
  6. The hitCallback is fired again, but this time the maximum number of repeats is reached and the function just returns an undefined value. Thus, no callback event is pushed into dataLayer, and the tag does not fire again.

I think this is an amazing feat of strength from dynamic macros and the callback function. This should make any enterprise setup much, MUCH leaner.

Back to top

{{true}} and {{false}}

This one’s a quicky but pretty important. Create two Custom JavaScript macros:

{{true}}

function() { return true; }

{{false}}

function() { return false; }

Why, you ask? Well, let’s say you want to set the Anonymize IP setting to true for one domain and false for two other domains (this idea came from Carmen Mardiros, by the way!). You want to use a Lookup Table Macro to achieve this, but when your Lookup Table aligns domains with the return value of “true” and “false”, you notice that things don’t work as you’d like them to. The problem is that if you add text to a field in GTM, it is always cast to string type. Anonymize IP requires Boolean type, otherwise it fails.

To circumvent this problem, instead of typing “true” and “false” into GTM’s fields, use the macros you just created. They return true and false in proper Boolean type! This way you will be able to pass the correct value types to the settings which require Boolean values.

Naturally, this should be fixed by adding Boolean true and false as possible values in all GTM fields.

Back to top

Conclusions

Well there you have it! Another round of GTM magic. I can’t wait to experiment more and uncover even more cool stuff to share with you. Be sure to join our amazing Google+ community which has served as inspiration for most of these tips.

Also, if you happen to be in any of the following upcoming conferences, come say hi to me:

ShareShare on Google+200Tweet about this on Twitter16Share on LinkedIn31Share on Facebook7

Comments

  1. Emil Venkov says

    Hi, Simo thank you for the gems :).
    I wonder something, in section Fire a single tag multiple times, where you define firing rules for the last tag ({{event}} equals gtm.js AND {{event}} equals tagCallback)), between the rules should be OR, right ?. Because now the tag will fire only if the {{event}} is simultaneously “gtm.js” and “tagCallback”, correct me if i am wrong.

    • says

      Oh man, I was sure that this time there was no room for error :)

      As I state in the text, you need to add two rules NOT two conditions. If a single rule has multiple conditions, then there’s the implicit “and” operator between them, which means that all conditions must be met for the rule to work. If, however, you use multiple rules, then there’s the perpetual “or” implied, meaning that the rule will fire whenever either one of the rules is met.

      The AND in the text wasn’t a logical operator, it was simply the conjunction “and” in the English language :) What I meant was exactly as I wrote: you need two rules (not two conditions): {{event}} equals gtm.js AND {{event}} equals tagCallback.

      I added a screenshot to clarify things.

      This is the problem when using terminology that isn’t too clear yet (especially with a relatively new product). Rule = set of conditions which must be met, condition = a part of a rule.

      Thanks for pointing out the ambiguity in the text.

      • Emil Venkov says

        Omg, sorry for the stupid comment :D, i guess the programming logic is too deep in my mind :D.

  2. says

    Hi Simo!
    Thanks for another great post. I really loved the “Fire a single tag multiple times” gems. This is brilliant and you explained it so simple!

    I have one question about the dataLayer – I have a click listener (for example) so I know gtm.element has pushed to the dL, but I’ve wondered how can I get this value without GTM (just for training).
    When I write dataLayer.gtm.element in the console it returns “TypeError: Cannot read property ‘element’ of undefined”.
    Of course if I write dataLayer[4] it returns the right object but why I can’t access this gtm.element directly?

    • says

      Well, it’s simply because dataLayer is of type Array. It’s not a single object with properties, but it’s a collection of objects with properties.

      So let’s imagine you click five times on your page. This means that five objects with property ‘gtm.element’ have been pushed to the dataLayer. If you tried to access it directly with dataLayer.gtm.element, which object would the browser know to return?

      When accessing an index in the array, you always need to specify its location. So if it’s the fifth object in the queue, you’d access it with dataLayer[4].

      You might have noticed that dataLayer[4].event works but dataLayer[4].gtm.element doesn’t. This is because when using dot notation, the browser interprets each dot-separated entity as its own property. That’s why you get the TypeError, since there’s no property “gtm” in the object.

      IF you want to access the object directly, you need to abandon dot notation and use bracket notation. So in the previous example, you’ll get the value of “gtm.element” by typing dataLayer[4]["gtm.element"]. This means “get the value of ‘gtm.element’ from the fifth index of the dataLayer Array”.

      The way the Data Layer Variable Macro works is that it circumvents this whole “which index, which property” thing by going backwards from the most recent Array index. So when you create a macro for gtm.element.parentElement.className, for example, GTM starts from the most recent object in the dL. If it finds “gtm.element.parentElement.className”, it returns its value and halts the process. If it doesn’t, it moves to the second most recent object in the queue and so forth.

      • says

        This is amazing. When I read this explanation I can feel my dopamine flowing in my blood. I’m serious.

        If I understand you properly, so when I use dL variable via GTM it does NOT interpret each dot-separated entity as its own property, but goes directly to the objects in the dL array?

      • says

        Well, actually Version 2 of the DL macro did bring the added functionality of dot notation. So I guess there’s a mechanism in place which tests for hierarchical objects (e.g. {gtm: {element: [object]}}) and for strings with dots, e.g. “gtm.element”.

        So GTM understands both: dot-separated objects AND strings with dots.

  3. says

    Hi Simo,

    I also need (multiple trackers on page) to set the Tracker name in GTM. However I noticed a warning below the option:
    “Use of named trackers is highly discouraged in GTM”

    This points to a section of a help page (https://support.google.com/tagmanager/answer/2574372#TrackerName) that makes absolutely no sense to me. They say to define the tracker names using the data layer, but then I still need to put those values in Google Tag Manager somewhere to link the code and the tags, so I’m gonna need to use the Tracker Name option anyway

    I’ve raised a question on StackOverflow, feel free to drop a comment or answer if you have the slightest clue as to what the warning means and how the code should be updated accordingly (if it needs to be):
    http://stackoverflow.com/questions/24425547/should-i-set-the-tracker-name-cookie-name-and-cookie-domain-for-my-tags

    Thanks,
    Max

Leave a Reply

Your email address will not be published. Required fields are marked *