It’s time to dig into my tip library for some pretty cool things you can do with Google Tag Manager to enhance your site tagging. Some of these are macro magic through and through, some of these are best practices, and some of these are things that will make your life easier while managing a tag management solution.

I’ve split this post into two parts to make it more Hobbit and less Lord Of the Rings length-wise.

Here’s what you’ll find within this first part:

  1. Unbind jQuery

  2. Undefined fields are not sent to GA

  3. Track copy event

  4. Errors as events

Keep your eyes peeled for the second part.

I know, the anticipation is probably killing you.

1. Unbind jQuery

I’m not a huge fan of jQuery. I mean, it’s definitely useful, since it simplifies a lot of the difficult and error-prone syntax that complex JavaScript entails. At the same time, I enjoy complex challenges, and learning the hard way has definitely made me a better developer.

In my experience, and this is definitely a very marginal view, jQuery introduces a certain degree of, erm, sloppiness. I know the “it’s too easy” argument is infantile, but in some cases the lack of transparency to what’s actually happening in the code can invite some pretty poor development work. In a way, jQuery is more about reading a manual than about actually creating code, and that sucks. Especially with event handlers, some development decisions are clearly influenced by how easy it is to bind the handlers to elements.

For Google Tag Manager, this is all too familiar. An innocuous return false; or e.stopPropagation() will prevent your GTM listeners from ever retrieving the DOM event. Often the only reason propagation is cancelled like this is the fact that the code was copy-pasted from some Stack Overflow discussion completely unrelated to your website’s markup.

OK, sorry for the rant. Here’s what I suggest. Whenever your GTM listener doesn’t fire when it should, open the JavaScript console and type the following line of code there…


…and press enter. Then try clicking or form submitting again.

What this simple line does is it unbinds all jQuery event handlers from all HTML elements in the document body. If your GTM listener works after this, it means that there’s some interfering jQuery script. The next step is to find the problematic script, and ask your developer to fix it so that propagation isn’t prevented.

(Be sure to check out my Chrome extension, where this feature is handily one button click away!).

Or you can choose the hacker route. If you find the interfering script, and if you find the function that’s grabbing your clicks or submits and preventing propagation, you can use a Custom HTML tag to fix it.

NOTE! This is a hack. Be sure to test, test and TEST before publishing a container where you unbind jQuery. Also, it wouldn’t hurt to consult with someone who has an understanding of just what event handlers are used on the site.

Let’s say you have a Back To Top button which is not sending clicks to GTM’s listeners. You find the code in one of the page assets, and it looks something like this:

jQuery('.top-of-page, a[href="#top"]').click(function () {
  jQuery('html, body').animate({
    scrollTop: 0
  }, 400);
  return false;

As you can see, the dreaded return false; is there. That’s what’s stopping the events from climbing up the DOM tree.

To fix this, create a new Custom HTML tag with the following code within:

  jQuery('.top-of-page, a[href="#top"]').off('click');
  jQuery('.top-of-page, a[href="#top"]').click(function(e) {
    jQuery('html, body').animate({
      scrollTop: 0
    }, 400);

If jQuery and the problematic script are loaded in the <head> of your template, you can have this tag fire on {{url}} matches RegEx .*. If they’re loaded elsewhere (like in the footer of your page), you might need some other event rule such as {{event}} equals gtm.load.

On the first line, I unbind the original click handler. Then, I add e as an argument of the new click function. This e contains the event object, and I can then use its preventDefault() method to stop the original action of the click without stopping its propagation. Finally, I remove the return false; from the end of the function.

Naturally, you’ll need to test, test, test and then test some more whether this hack breaks down your entire site or not.

Of course, it’s not just jQuery that might interfere with your listeners. It might be vanilla JavaScript or some other library. This chapter focuses on jQuery simply because it’s so widely spread that most of the time it really is the source of the problem.

2. Undefined fields are not sent to GA

Here’s a fun fact about the Google Analytics API: if a field such as Custom Dimension is undefined when the GA endpoint is called, the Custom Dimension is left out of the payload. How is this fun? Well, it introduces an opportunity to make your tags a bit more dynamic.

It would be nice to get a list of all the fields that behave this way, and also how they behave with other return values such as false or 0.

So, let’s say you have a Custom Dimension such as blog author that you want to send with your page view tags. Naturally, you only set the blog author on blog pages. You don’t want to send a dummy value like “empty” or “n/a” on other pages on your site, so you’re absolutely certain that you need two tags: one when the blog author is populated (and can be sent as a Custom Dimension), and one on all the other pages (where the Custom Dimension will be left out of the tag).

However, thanks to how the GA API works, you only need one tag. Just make sure that the macro which returns the Custom Dimension value resolves to undefined type when the dimension doesn’t have a value. There are a bunch of ways you can do this:

function() {
  var t;
  var y = "";
  return t; // returns undefined type
  return; // returns undefined type
  return undefined; // returns undefined type
  return false; // returns boolean type
  return y; // returns string type
  return x; // throws ReferenceError

As you can see, returning false, an empty string, or a variable which hasn’t been declared will not do.

The cool thing about GTM is that if you have a Data Layer Variable Macro which doesn’t resolve, it will be automatically set to undefined type, meaning a Custom Dimension which refers to this macro will not get sent. So the best way to navigate through this particular scenario is to have blog-author declared in the dataLayer declaration before the container snippet on the page template. If the page isn’t a blog page, then this declaration can just be left out.

  var dataLayer = [{'blog-author': 'Simo Ahava'}]; // Blog author declared, dimension will get sent
  var dataLayer = []; // Blog author not declared, dimension will not get sent

Then, when you create a Data Layer Variable Macro, on pages with blog-author in the array, it will fire the Custom Dimension with the blog author’s name as the value. On all other pages the dimension will not get sent.

Once you familiarize yourself with JavaScript and understand all the different ways how GTM and JavaScript resolve variables to undefined type, you can get really creative with this feature, making your tags even more leaner.

3. Track copy event

Ever wondered how often people copy text using CTRL+C or some other means? Well, wonder no more, because now you can track it as an event!

Create this simple Custom HTML tag:

  var c = document.getElementById("entry-content");
  if(typeof(c)!=='undefined') {
    c.addEventListener('copy', function(evt) {
      dataLayer.push({'event': 'copy'});

This is a very simple script that pushes the dataLayer event ‘copy’ whenever someone copies something on your page within the HTML element with ID entry-content (e.g. <div id="entry-content"></div>).

Be sure to change the element whose content you want to track with the event listener to match the markup on your website.

You can make it more versatile by exploring the contents of the evt argument, which is the event object. Its target property contains the HTML element which was the target of the event. Here are some ideas:

dataLayer.push({'event': 'copy', 'copy-id':, 'copy-content':});

And so on. Note that stores the element where the copy event started. So if the copy action takes place over multiple paragraphs, headers, or spans, for example, you’ll only be able to observe the properties of the first element targeted by the action.

4. Errors as events

Google Tag Manager has this cool thing called the JavaScript Error Listener. This fires whenever an unchecked exception occurs on the site. For example, consider the following:

var myText = "Hello!";
alert(myTxt); // Unchecked ReferenceError, will fire GTM's error listener

// Exception caught, will not fire GTM's error listener
try {
} catch(e) {

With GTM’s error listener, you can send each uncaught error as an event to Google Analytics. You can then analyze these events to find problems with your scripts. If you have a huge site, you might get hundreds of these errors, so you’ll need to adjust the code to perhaps only listen for certain pages or errors.

I’m hoping at some point we can have the listener fire only for errors propagated by GTM’s tags and macros. This way you can debug your GTM installation without having to worry about all the other errors on your website (even though you definitely should!).

To get the listener up and running, do the following:

  1. Create a new JavaScript Error Listener tag

  2. Have it fire on all pages (or some other rule of your choice)

  3. Create a new rule Event - gtm.pageError, where {{event}} equals gtm.pageError

  4. Create three new Data Layer Variable macros:

    1. {{Error - Message}}, where variable name is gtm.errorMessage

    2. {{Error - Line}}, where variable name is gtm.errorLineNumber

    3. {{Error - URL}}, where variable name is gtm.errorUrl

  5. Create a new Event tag and configure it to your liking

  6. Make sure the Event tag fires on the rule you created in (3)

Remember to set the Non-Interaction Hit parameter of the Event tag to true. You don’t want to kill your bounce rate with errors on your site!

If you’re seeing a lot of Script error. hits, it means that the errors occur in JavaScript resources loaded from other domains. Due to browser / cross-domain security policies the actual error name and message are obfuscated.


The second part of this post is on its way. I didn’t want to puke too much text into a simple tips & tricks post, but this one is 1800+ words already, so… :) Topics in the next post are:

  1. dataLayer declaration vs. dataLayer.push()

  2. How to stop a GTM timer listener

  3. Adopting a proper naming convention

  4. Annotating container versions with names and notes

Anyway, I love nothing better than to explore the versatility of Google Tag Manager. There’s so much you can do with JavaScript, DOM API, and the Google Tag Manager library!

All that’s required is a bit of know-how with JavaScript and the Document Object Model, a lot of creativity, and a lot of testing, previewing and debugging.