Saturday, February 22, 2025
HomeAnalyticsCSS Selector Guide For Google Tag Manager

CSS Selector Guide For Google Tag Manager


. Since it has the id attribute, it’s a reasonable expectation that it’s the only such element on the page, thus even that short selector is very powerful.

On the other hand, sometimes all you can do is use a really long, complex chain, because there are so few uniquely identifying features in the element itself or its closest ancestors. Thus, the selector might end up looking like this:

#main > article > div.post-content.markdown > div > p:nth-child(12) > a:nth-child(2)

It’s not pretty, and it carries the additional weight that the longer and more complex the CSS selector is, the more fragile it becomes. The selector above will be invalidated when any one of the elements in the chain changes position or form.

In other words, always strive to create a selector that is as simple as possible without compromising its accuracy to target the exact element you want.

CSS selectors originated, surprise surprise, in Cascading Style Sheets (CSS). Style sheets are sets of rules and declarations that govern how HTML elements are displayed (and sometimes interacted with) on a web page. Here’s what a typical style declaration might look like:

a:hover, a:active {
    text-decoration: none;
    font-weight: bold
}

The two items preceding the { and separated by a comma are the selectors, and the two rows contained within the curly braces are the declarations. This is how you would read the rule:

  1. Select all links (a) that are currently being hovered over by the mouse (:hover)
  2. AND select all links (a) that are currently being clicked by the mouse (:active)
  3. THEN remove all text decorations (e.g underline) from them
  4. AND set their font weight to bold

CSS is its own, wonderfully complex discipline in web design and development. There are so many weird and magnificent things you can do with it, from replacing JavaScript functionality to running complex transitions using hardware acceleration.

Well, in this guide we’ll try to be a bit more modest and focus on how CSS selectors can be used with Google Tag Manager’s JavaScript to make the most out of triggers, tags, and variables, where selecting, querying, or parsing the correct element is of utmost importance.

CSS selectors in JavaScript

In JavaScript, you’ll often run into CSS selectors in two scenarios:

  1. You need to retrieve a specific element or elements from the page.

  2. You need to check if a given element matches a CSS selector.

Both of these scenarios are very relevant in Google Tag Manager. Before we jump into how they work in GTM, let’s take a look at JavaScript’s handling of CSS selectors.

Using document.querySelector and document.querySelectorAll

The two JavaScript methods you’ll most often use with CSS selectors are document.querySelector(selector) and document.querySelectorAll(selector).

The first returns the first element on the page that matches the given selector.

The second returns a list of all the elements on the page that match the given selector.

// Get the first outbound link element on the page
var firstOutbound = document.querySelector('a:not([href="mydomain.com"])');

// Get all the checked checkbox and radio button elements on the page
var allChecked = document.querySelectorAll('input[type="checkbox"]:checked,input[type="radio"]:checked');

As you can see, the selector is passed as a string argument to the method.

Remember that HTML elements are essentially objects, and just capturing them makes often little sense. Instead, you’ll want to do something with those objects, such as parse some property from them.

// Get the first outbound link element on the page
var firstOutbound = document.querySelector('a:not([href="mydomain.com"])');

// Push the URL of this outbound link into dataLayer
window.dataLayer.push({
  firstOutboundLinkURL: firstOutbound.getAttribute('href')
});

In the example above, we use document.querySelector to fetch the first outbound link (i.e. link that does NOT have mydomain.com in its href), and then we push it into dataLayer.

One thing to remember is that document.querySelectorAll doesn’t return an array but rather a NodeList. This means that you can’t use regular array methods with whatever the method returns. You’ll have to resort to some workarounds if you, for example, want to map() all elements in the list to get a modified array as a result.

// Get ALL outbound links on the page
var allOutbound = document.querySelectorAll('a:not([href="mydomain.com"])');

// Create an array of all their href values
var allHrefValues = Array.prototype.map.call(allOutbound, function(link) { return link.href; });

// Remove all duplicates
var uniqueHrefValues = allHrefValues.filter(function(href, index) { return allHrefValues.indexOf(href) === index; });

As you can see, in order to run the map() method against the list returned by document.querySelectorAll, you need to invoke the method from the array prototype. It’s a good thing to keep in mind if you ever find yourself working with document.querySelectorAll.

Using element.matches

If you want to check if any given element matches a specific CSS selector, you can use the matches() method like this:

// Check if the clicked element is an outbound link
function checkIfClickedElementIsOutbound() {
  var element = {{Click Element}};
  if (element.matches('a:not([href="mydomain.com"])') {
    return true;
  } else {
    return false;
  }
}

You invoke the matches() method on the element itself (the element has to be an HTML element), and like querySelector / querySelectorAll, you pass the selector as a string argument. The method returns true for a match, and false otherwise.

The thing about matches() is that it didn’t use to have stellar browser support, so if you want to use these methods with Internet Explorer in mind, you might need to implement a polyfill to extend the support (see here for inspiration).

Luckily, the matches() method is abstracted in Google Tag Manager when using triggers, which is by far the most common use case. So let’s dive right in!

CSS selectors in Google Tag Manager

In Google Tag Manager, you’ll find CSS selectors in a number of places.

You can use them as the selection method in the DOM Element variable:

Using a CSS selector here lets you target specific elements that might not have the ID attribute (the only other selection method provided).

You can also find the same option in the Element Visibility trigger:

Whereas the DOM Element variable only returns the first matching element (thus being similar to document.querySelector), the Element Visibility trigger can be set to target all the matching elements, making CSS selectors super powerful in this particular trigger type.

The third, and by far the most useful scenario for CSS selectors in Google Tag Manager is the matches CSS selector predicate when used together with the {{Click Element}} built-in variable.

The Click Element variable

The Click Element variable is a Built-in variable that you need to enable before you can use. The easiest way to enable it is to go to Variables via GTM’s main navigation, and click the blue Configure button in the top corner of the content.

In the overlay that opens, check the box next to Click Element and you’re done. Now you can choose the Click Element variable in all the variable drop-downs of GTM.

Click Element returns the HTML element that was the target of the auto-event trigger action. In other words:

  • It returns the clicked element when using the Click / All Elements and Click / Just Links triggers.

  • It returns the submitted form element when using the Form Submission trigger.

  • It returns the matched element that became visible when using the Element Visibility trigger.

Because it returns an HTML element, you can use it in your Custom HTML tags and Custom JavaScript variables together with typical HTML element methods such as getAttribute, appendChild, and yes, matches.

In GTM, you’ll use it most often, I’m confident enough to say, with the matches CSS selector predicate in your Click and Form triggers. It’s not that useful with the Element Visibility trigger, since you already specify the set of matching elements with the CSS selector setting in the trigger.

The Matches CSS Selector predicate

You can use the matches CSS selector trigger predicate together with the Click Element variable to check if the element matches a specific selector (d’oh).

This is particularly useful with the All Elements trigger, since it truly fires when anything on the page is clicked, and you can use CSS selectors to delimit the tag to which the trigger is attached from firing all too often.

To make it work, you need to first check the This trigger fires on…Some Clicks/Some Link Clicks/Some Forms option, then select the Click Element variable from the variable selector, the matches CSS selector predicate from the predicate selector, and then type the selector in the field to the right, like so:

One thing to keep in mind is that the All Elements truly captures the exact element the user clicked. Thus it might be something nested within the element you actually drafted the selector for. With the All Elements trigger, you should make liberal use of the wildcard (*) selector. Read this article for more information.

In the reference chapter below, I have included what a Click trigger would look like for all the selectors listed in the reference.

CSS selector reference for Google Tag Manager

The reference below is a modified (slimmed down) version of the w3schools’ excellent resource. I’ve tailored the different selectors with imaginary Google Tag Manager use cases in mind.

Remember that you can combine selectors to identify different aspects of the element. When combining selectors, put them one after the other with no whitespace in between. For example, to target all elements that have the author class, simo as the ID, and that are also outbound links, use something like this:

a.author#simo:not([href="mydomain.com"])

The order is inconsequential, but it’s customary to add pseudo-classes (e.g. :checked, :not) to the end for readability.

.class

Matches elements that have the given class as one of the class names in their class attribute.

Sample HTML structure

a class="highlight author" href="/author-page/">
  span>Simo Ahavaspan>
a>

Sample trigger

Selector

.author

Outcome

The trigger fires if the link is clicked. Clicks on the work too, since the Just Links trigger automatically retrieves the closes wrapping element of whatever was actually clicked.

#id

Matches elements that have the given ID as the value of their id attribute.

Sample HTML structure

div class="date">
  a href="/archives">
    span id="date">2019-04-09span>
  a>
div>

Sample trigger

Selector

#date

Outcome

The trigger fires if the ... element is clicked.

element

Matches elements that are the given element (e.g. a for links, img for images).

Sample HTML structure

a class="author" href="/author-page">
  img id="simo" src="simo.jpg"/>
a>

Selector

img#simo

Sample trigger

Outcome

The trigger fires if the clicked element is the . As you can see, the selector combines both element and id, meaning the matched element must be both an image and have the ID simo.

element,element

Matches elements that can be selected with ANY of the selectors separated by a comma (you can add multiple selectors, each separated by a comma).

Sample HTML structure

a class="author" href="/author-page">
  img id="simo_img" src="simo.jpg"/>
  p id="simo_name">Name: Simo Ahavap>
a>

Sample trigger

Selector

img#simo_img, p#simo_name

Outcome

The trigger fires if the click lands on EITHER OR

...

.

element element

Matches the rightmost element when it is within the leftmost element in the DOM tree. The relationship does not have to be parent-child – it’s enough that the left element wraps the right element at some point.

Sample HTML structure

section id="main">
  div id="article">
    a href="/archives">
      h1>Hello world!h1>
    a>
  div>
section>

Sample trigger

Selector

section#main h1

Outcome

The trigger fires if the click lands on because one of its wrapping elements matches the first selector (section#main).

* (wildcard)

Matches any element. Extremely useful when used with the Click / All Elements trigger.

Sample HTML structure

div id="navi">
  ul>
    li>a href="/home">Homea>li>
    li>a href="/products">Productsa>li>
    li>a href="/contact">Contact usa>li>
  ul>
div>

Sample trigger

Selector

div#navi, div#navi *

Outcome

The trigger fires when the

element or any of its nested elements (,

  • and ) are clicked.

    The selector is div#navi, div#navi *. Based on what you have learned above, this is actually two selectors, div#navi and div#navi *, and the trigger will fire if either is clicked.

    The first selector matches clicks directly on the

    element. The second selector matches clicks on any element that is wrapped by

    . The whitespace is important here. If the second selector were div#navi > *, it would only match clicks on the , as it’s the only direct child of

    .

    element>element

    Matches the rightmost element when its direct parent is the element to the left.

    Sample HTML structure

    section id="main">
      div id="article">
        a href="/archives">
          h2>Hello world!h2>
        a>
      div>
    section>
    section id="end">
      div id="footer">
        h2>Goodbye world!h2>
      div>
    section>

    Sample trigger

    Selector

    div>h2

    Outcome

    The trigger fires if the click lands on

    Goodbye world!

    because its direct parent is

    . It will not fire on

    Hello world!

    because that element’s direct parent is .

    element+element

    Matches the rightmost element if it comes directly after the leftmost element. They must share the same parent.

    Sample HTML structure

    div id="author">
      img src="/author.jpg" id="author_image"/>
      p>Author namep>
    div>

    Sample trigger

    Selector

    img#author_image+p

    Outcome

    The trigger fires if the click lands on the , because it is right after the .

    element1~element2

    Matches the rightmost element if it is preceded by the leftmost element. They must share the same parent. In other words, it’s slightly less strict than the previous selector, in that the preceding element does not have to be immediately next to the targeted element.

    Sample HTML structure

    div id="author">
      img src="/author.jpg" id="author_image"/>
      span>Author namespan>
      a href="https://www.simoahava.com">Author home pagea>
    div>

    Sample trigger

    Selector

    img#author_image~a

    Outcome

    The trigger fires if the click lands on the , because it is preceded by .

    [attribute]

    Matches if the element has the given attribute.

    Sample HTML structure

    div id="main_content">
      div data-name="gtm_example">
        span>GTM examplesspan>
      div>
    div>

    Sample trigger

    Selector

    div[data-name]>span

    Outcome

    The trigger fires if the click lands on the , because its direct parent is a

    with the data-name attribute.

    [attribute=value]

    Matches if the element has the given attribute with the exact value.

    Sample HTML structure

    div id="main_content">
      div data-name="gtm_example">
        span>GTM examplespan>
      div>
      div data-name="second_gtm_example">
        span>Another GTM examplespan>
      div>
    div>

    Sample trigger

    Selector

    div[data-name="second_gtm_example"]>span

    Outcome

    The trigger fires if the click lands on the second , because only it has a direct parent whose data-name attribute has the value second_gtm_example.

    [attribute^=value]

    Matches if the element has the given attribute whose value starts with the provided string.

    Sample HTML structure

    div id="main_content">
      span id="product_12345">
        Product 12345
      span>
      span id="product_23456">
        Product 23456
      span>
      span id="attachment_12345">
        Attachment 12345
      span>
    div>

    Sample trigger

    Selector

    span[id^="product"]

    Outcome

    The trigger fires if the click lands on either of the two elements whose ID starts with product. It will not fire if the click lands on the third span, because that element ID starts with attachment.

    [attribute$=value]

    Matches if the element has the given attribute whose value ends with the provided string.

    Sample HTML structure

    div id="main_content">
      span id="product_12345">
        Product 12345
      span>
      span id="product_23456">
        Product 23456
      span>
      span id="attachment_12345">
        Attachment 12345
      span>
    div>

    Sample trigger

    Selector

    span[id$="_12345"]

    Outcome

    The trigger fires if the click lands on either the first or the last , since both elements’ ID attributes end with _12345. It will not fire if the click lands on , since that ID does not end with _12345.

    [attribute*=value]

    Matches if the element has the given attribute whose value contains the provided string.

    Sample HTML structure

    div id="author">
      span id="simo_ahava_profile">Simo's profilespan>
      span id="profile_benjamin_ahava">Benjamin's profilespan>
      span id="derek_anderson">Derek's profilespan>
    div>

    Sample trigger

    Selector

    div#author > span[id*="ahava"]

    Outcome

    The trigger fires if the click lands on either of the first two elements, since they have IDs that contain the string ahava. It will not fire for the third because that element’s ID attribute does not contain the string ahava.

    :checked

    Matches if the given element is checked (radio buttons, checkboxes, and elements within select menus).

    Sample HTML structure

    form id="contact-us">
      My name: input type="text" name="name" />br />
      I consent to everything input type="checkbox" name="consent" />br />
      input type="submit" />
    form>

    Sample trigger

    Selector

    input[name="consent"]:checked

    Outcome

    The trigger fires if the click lands on the checkbox when it is checked (i.e. the user unchecks it).

    :first-child

    Matches if the given element is the first child of its parent.

    Sample HTML structure

    ul id="main_navigation">
      li>Homeli>
      li>a href="/products">Productsa>li>
      li>a href="/contact">Contacta>li>
    ul>

    Sample trigger

    Selector

    ul#main_navigation > li:first-child

    Outcome

    The trigger fires if the click lands on

  • Home
  • since it is the first child element of its direct parent.

    :first-of-type

    Matches if the given element is the first element of its type (e.g. p, img, span) of its parent.

    Sample HTML structure

    div id="author_info">
      a href="/author">Simo Ahavaa>
      p>Web Analytics Developerp>
      p>Ukulele and death metal enthusiastp>
    div>

    Sample trigger

    Selector

    div#author_info > p:first-of-type

    Outcome

    The trigger fires if the click lands on the

    Web Analytics Developer

    since it is the first under its parent.

    :last-child

    Matches if the given element is the last child element of its parent (the reverse of :first-child).

    Sample HTML structure

    ul id="main_navigation">
      li>GTMli>
      li>Ukuleleli>
      li>Death metalli>
    ul>

    Sample trigger

    Selector

    ul#main_navigation > li:last-child

    Outcome

    The trigger fires if the click lands on

  • Death metal
  • since it is the last child element of its parent.

    :last-of-type

    Matches if the given element is the last element of its type of its parent.

    Sample HTML structure

    div id="author_info">
      a href="/author">Simo Ahavaa>
      p>Web Analytics Developerp>
      p>Ukulele and death metal enthusiastp>
    div>

    Sample trigger

    Selector

    div#author_info p:last-of-type

    Outcome

    The trigger fires if the click lands on

    Ukulele and death metal enthusiast

    since it is the last under its parent.

    :not(selector)

    Matches if the given element does not match the selector within the parentheses.

    Sample HTML structure

    div id="author">
      p>
        a href="https://www.simoahava.com/">Home pagea>
      p>
      p>
        a href="https://tagmanager.google.com/">Favorite hobbya>
      p>
    div>

    Sample trigger

    Selector

    a:not([href*="simoahava.com])

    Outcome

    The trigger fires when the click lands on the second link (Favorite hobby). This is because the CSS selector targets all link elements () that do not match the CSS selector [href*="simoahava.com"]. In other words the link must not have a href attribute containing the string simoahava.com.

    :nth-child(n)

    Matches if the given element is the nth child of its parent, where n is the number passed in the parentheses.

    Sample HTML structure

    ul id="main_navigation">
      li>GTMli>
      li>Ukuleleli>
      li>Death metalli>
    ul>

    Sample trigger

    Selector

    ul#main_navigation > li:nth-child(2)

    Outcome

    The trigger fires if the click lands on

  • Ukulele
  • since it is the second child (nth-child(2)) of its parent.

    Summary

    I’ve said it many times, but I seriously think CSS selectors are high up there with JavaScript and regular expressions in the list of things you must learn if you want to master Google Tag Manager.

    Much of browser-based web analytics, especially when tracking interactions, is rooted in the ability to track interactions with specific elements. Often, we’re recommended to use the id and class attributes these elements have, but it’s very common that they don’t actually have these attributes.

    It’s in these situations that CSS selectors shine. They allow you to pinpoint every single and any element on any given page, since every element always has a unique position that can be targeted with some selector.

    Just remember my tip from earlier - always strive for a selector that is a simple as possible without compromising its ability to target the specific elements you want it to.


    © 2025 Simo Ahava. All Rights Reserved

    Author's picture

    Simo Ahava

    Husband | Father | Analytics developer
    simo (at) simoahava.com



    Senior Data Advocate at Reaktor



    Finland

    RELATED ARTICLES

    LEAVE A REPLY

    Please enter your comment!
    Please enter your name here

    Most Popular

    Recent Comments