← All Guides

GTM Triggers and Variables Fundamentals: Clicks, Visibility, Scroll, Timers, Custom Events and the Variables That Make Them Useful

A working reference to Google Tag Manager's trigger types and variable types, when each one is the right tool, the built-in variables to enable first, and code-free patterns for tracking buttons, outbound links, downloads, scroll depth and time on page.

Atiqul Islam
Written by Atiqul Islam

Tracking & analytics specialist. 278+ GTM, GA4 and server-side tracking projects for clients in the US, UK and Australia.

Rather not do this yourself? I set this up for clients every week. Free 15-minute audit, no obligation.

Book a Free Audit

Google Tag Manager has a short list of trigger types and a short list of variable types, and nearly every tracking requirement is some combination of them. This guide is the reference I wish I had when I started: each trigger type, what it is good for, the variables that pair with it, and the patterns I set up on almost every site.

Enable the Built-In Variables First

In Variables, click Configure and enable: Page URL, Page Path, Page Hostname, Referrer; Click Element, Click Classes, Click ID, Click Target, Click URL, Click Text; Form Element, Form ID, Form Classes, Form URL; Scroll Depth Threshold, Scroll Direction; Percent Visible, On-Screen Duration; Video Provider, Video Status, Video Percent, Video Title. These are free; triggers cannot use them until they are on.

Trigger Types and When to Use Them

Trigger Fires when Use it for
Consent Initialization Before anything else Consent platform tags only
Initialization Before page view triggers Google tag, base pixels
Page View GTM loads Standard page tags
DOM Ready HTML parsed Tags that read page elements
Window Loaded Everything loaded Listeners that depend on other scripts
Click, All Elements Any click Buttons, non-link elements
Click, Just Links Click on an anchor Outbound, downloads, phone, email links
Element Visibility An element is on screen Success messages, sections viewed, CTAs seen
Form Submission Browser submit event Only with validation; prefer dataLayer
Scroll Depth Percent or pixel thresholds Content engagement
YouTube Video Embedded YouTube events Play, progress, complete
Timer Interval elapsed Time on page milestones
History Change URL changes without reload Single page apps
JavaScript Error Uncaught error Monitoring
Custom Event dataLayer push with event key Every business event
Trigger Group Several triggers have all fired Combined conditions

Variable Types and When to Use Them

Variable Returns Use it for
Data Layer Variable A key from the dataLayer Everything the site pushes
URL Parts of the current URL or query parameters Campaign parameters, thank-you page form parameter
First-Party Cookie A cookie value gclid storage, consent state, user flags
DOM Element Text or attribute of an element Reading page data when no dataLayer exists
Auto-Event Variable Click or form element attributes Rarely needed beyond built-ins
Lookup Table and RegEx Table Mapped values Page path to section, form ID to form name, event to conversion label
Custom JavaScript Anything computed Business hours, mapping arrays, normalising values
JavaScript Variable A global JS variable Reading values the site exposes on window
Constant A fixed string Measurement IDs, conversion IDs
Google Analytics Settings and User-Provided Data Grouped config Shared GA4 settings, Enhanced Conversions

Patterns That Belong on Almost Every Site

Outbound link clicks

Trigger: Click, Just Links. Condition: Click URL does not contain your hostname, and Click URL starts with http. Tag: GA4 Event click with link_url set to Click URL and link_text to Click Text. Enhanced measurement does this too; if it is on, skip this.

File downloads

Trigger: Click, Just Links. Condition: Click URL matches RegEx \.(pdf|docx?|xlsx?|pptx?|zip|csv)$. Tag: GA4 Event file_download with file_name from a Custom JavaScript variable that returns the last path segment.

Phone and email clicks

Trigger: Click, Just Links. Condition: Click URL starts with tel:. Tag: GA4 Event phone_click with phone_location from a Lookup Table keyed on Click Classes or a parent section. A second trigger for mailto: and an email_click event. Add Google Ads conversion tags on the phone trigger where calls matter.

Button clicks that are not links

Trigger: Click, All Elements. Condition: Click Element matches CSS selector button.cta, button.cta *, a.btn-primary, a.btn-primary *. Tag: GA4 Event cta_click with cta_text from Click Text.

Where you can, ask for data-track attributes on the buttons, for example data-track="hero_cta", and read them with an Auto-Event Variable or DOM Element variable. Classes change; data attributes survive redesigns.

Section or CTA viewed

Trigger: Element Visibility, CSS selector #pricing, once per page, 50 percent visible, observe DOM changes. Tag: GA4 Event section_view with section_id set to the selector.

Scroll depth

Trigger: Scroll Depth, vertical percentages 25, 50, 75, 90. Tag: GA4 Event scroll_depth with percent_scrolled from Scroll Depth Threshold. Enhanced measurement covers 90 percent only; use this if you want the curve. Fire only on content pages with a Page Path condition to avoid noise.

Time on page milestones

Trigger: Timer, interval 30000 ms, limit 4, on content pages. Tag: GA4 Event time_on_page with the elapsed seconds from a Custom JavaScript variable that reads the timer's event number. Useful for content sites; skip elsewhere.

Single page app page views

Trigger: History Change. Tag: GA4 Event page_view with page_location and page_title. Set the Google tag to not send its own page view. See the SPA guide for the full approach.

Trigger Conditions That Save You

Folders and Naming

Prefix by type so the list sorts: GA4 - Event - phone_click, GAds - Conversion - Lead, Meta - Lead, CE - form_submit_success, Click - tel link, dlv - form_name, LT - form id to name. Put each client's or each platform's assets in a folder. The GTM Container Hygiene guide on this site goes further.

Test Every Trigger

In Preview, perform the action and watch the left column. Click the event, open the Variables tab and confirm the values the trigger needed. Then open the tag and confirm it fired once. A trigger that fires on every click, or never, is almost always a condition with a typo or an element selector that is too broad or too narrow.

Outcome

With built-in variables on, a handful of well-scoped triggers, and the dataLayer for business events, you can track nearly any interaction on a site without touching its code, and you will know exactly why each event fired.

Frequently Asked Questions

What is the difference between a trigger and a variable?

A trigger decides when a tag fires. A variable holds a value, such as the click URL or a dataLayer key, that triggers can test and tags can send. Most tracking is a trigger that watches an action plus variables that describe it.

Which built-in variables should I enable?

Page URL, Page Path, Page Hostname, Referrer, Click Element, Click Classes, Click ID, Click URL, Click Text, Form ID, Form Classes, Scroll Depth Threshold, Percent Visible, and the Video variables if you track YouTube. Enabling them costs nothing until a trigger uses them.

Click, All Elements or Click, Just Links?

Just Links for anchors, because it waits for the link to be followed and handles the navigation delay. All Elements for buttons, divs and anything that is not a link. Using All Elements for links can lose events when the page navigates before the tag sends.

Why does my click trigger fire on the icon but not the button?

Click Element is the innermost element clicked. If the visitor clicks the icon inside the button, the element is the icon. Use the matches CSS selector operator with a selector that covers descendants, such as button.cta, button.cta *.

Should I track everything with triggers instead of asking for a dataLayer?

No. Triggers that read the page are fine for navigation-type actions: outbound links, downloads, phone clicks, scroll depth. Business events such as purchases and form success should come from the dataLayer, because page structure changes and the dataLayer contract does not.

Want This Done Right the First Time?

Book a free 15-minute audit call. I'll review your setup, show you what is broken, and tell you exactly what it takes to fix it.

Chat Now