TutorialPractitioner7 min read

Form, click and scroll tracking in GTM

Use GTM click, form and scroll triggers wisely, know when enhanced measurement is enough, and when a data layer event is required. Nov 2026.

Dipuck Jones
Written by Founder of MentionX · 13+ years in B2B SaaS growth and SEO
Reviewed
Short answer

Click, form and scroll tags answer product and UX questions — which CTA gets attention, whether forms complete, how far people read. They are not a substitute for conversion events. Use Google tag enhanced measurement when it fits; use GTM triggers when you need custom definitions; escalate to the data layer when the DOM lies.

Key takeaways
  • Prefer enhanced measurement for standard scroll, outbound click, form interaction and file download before duplicating in GTM.
  • Use Click triggers for labelled CTAs; never as the only definition of revenue conversions.
  • Form Submission triggers fail on many modern embeds — verify in Preview.
  • Name interaction events clearly (cta_click_header_demo) and send useful parameters.

Checked 5 November 2026 (CET) against Google Tag Manager Help, Google tag platform developer docs, Google Ads Help, Analytics Help and Meta Business Help where cited. Google Tag Manager, GA4 and Ads UIs change often, so confirm labels in your own accounts. "Our view" marks MentionX opinion. Ledgerline is a fictional company used for illustration.

01Start with enhanced measurement

The Google tag can automatically detect scrolls, outbound clicks, form interactions, video engagement and file downloads (see Google tag settings Help). Before building five GTM scroll tags, check what is already flowing into GA4 (GA4 events and enhanced measurement).

Duplicate scroll tags waste hits and confuse analysts.

02Click tracking pattern

  1. Enable built-in Click variables.
  2. Create a trigger Click - All Elements when Click Classes contains btn-demo OR Click Text equals your CTA (know the fragility).
  3. GA4 Event tag name cta_click with parameters link_text, link_url, cta_location (hard-code location via Constant if needed).
  4. Preview; click once; confirm one event.

Better: ask engineering for data-cta="header-demo" attributes and read them with an Auto-Event Variable.

03Form tracking pattern

Try this order:

  1. Watch Preview while submitting — is there a Form event?
  2. If yes, Form Submission trigger + GA4 form_submit (careful: may fire on failed validation).
  3. If no, Element Visibility on success message, thank-you Page Path, or data layer generate_lead.

For primary leads, always graduate to generate_lead (lessons 6–8).

04Scroll tracking pattern

Enhanced measurement’s scroll fires at bottom. For 25/50/75/90 thresholds, use GTM Scroll Depth triggers or Element Visibility on landmarks (pricing table, FAQ). Send percent_scrolled as a parameter. Do not fire Ads conversions on scroll.

05Video and file downloads

YouTube embeds with API support can use YouTube Video triggers or enhanced measurement video events. File downloads: enhanced measurement catches common extensions; custom file types may need Just Links triggers filtered by URL ends with .pdf.

06Worked example: Ledgerline

Ledgerline is a fictional accounts-payable software company. Details are illustrative.

Ledgerline tracked every nav click for a month and produced a dashboard nobody used. They cut to three CTA events (header_demo, pricing_demo, footer_demo) plus generate_lead. Decision quality improved because the data got quieter.

Their HubSpot form never fired GTM Form Submission; they stopped fighting it and used the data layer success push.

Walkthrough

  1. Inventory enhanced measurement toggles currently on.
  2. Add one cta_click event for the primary header CTA.
  3. Attempt a Form Submission trigger on your real demo form; document pass/fail.
  4. If fail, create a ticket for data layer success (link the dictionary row).
  5. Publish only the CTA tag if the form path is still temporary.

07Designing a CTA taxonomy

Agree names before building tags:

  • cta_click event
  • parameters: cta_id, cta_location (header,hero,pricing,footer), cta_text (optional)

Then implement data-cta-id in the design system. One afternoon with design/engineering prevents a year of Click Text fragility.

08Scroll myths in B2B

High scroll depth on a long blog post is not intent to buy. Use scroll for content UX, not bidding. If a stakeholder wants scroll as a conversion, show them how it would bid on readers who never saw pricing.

09Form abandon analytics

To study abandon, you need intentional events (form_start, form_field_error) — not guesses from scroll. Scope that as a project with UX research, not a sneaky GTM-only hack that records field-level PII.

10Click trigger types compared

TriggerBest forWatch-outs
All ElementsButtons, divs, spans acting as buttonsCaptures too much if unfiltered
Just LinksTrue anchor link clicksMisses button-as-link patterns
Form SubmissionNative form submit eventsOften missing on JS frameworks
Element VisibilitySuccess messages, sticky CTAsCan re-fire; set once-per-page carefully
Scroll DepthArticle engagement thresholdsEasy to over-instrument

11Hardening selectors

Prefer, in order: data-* attributes, stable design-system classes, IDs owned by marketing pages, then visible text. Never key a production conversion off a CSS class that a designer renames during a brand refresh without a QA gate.

12Interaction events vs conversions (again)

cta_click informs UX. generate_lead informs pipeline and bidding. If leadership asks to "just bid on CTA clicks", show them the ratio of CTA clicks to qualified opportunities for the last month. Usually the gap kills the idea.

13SPA form gotchas

On single-page apps, a "page" may not reload after submit. Thank-you Page Path triggers never see a new path. Element Visibility on a success toast or a data layer push is mandatory. Test the real React/Vue/Angular flow in Preview — not a static HTML mock.

14Outbound and mailto/tel clicks

Enhanced measurement can capture outbound clicks. For tel: and mailto: links that matter to lead-gen sites, add explicit Just Links triggers filtered by Click URL starts with tel: / mailto: and send contact_click with method=phone|email. Do not mark these as Ads primary conversions unless call reporting is intentionally set up that way.

15Heatmaps vs GTM clicks

Hotjar/Clarity-style tools already show click maps. Duplicating every click into GA4 rarely beats sampling a week of heatmap sessions for UX questions. Use GTM click events for named CTAs tied to campaigns, not for reconstructing a heatmap inside Explorations.

16Building one CTA event the right way

Worked build for a header Demo CTA:

  1. Ask design for data-cta-id="header-demo" on the button/link.
  2. Enable Click Element / Click Classes / Click ID built-ins as needed; create an Auto-Event Variable for data-cta-id if you use that pattern.
  3. Trigger: Click - All Elements where data-cta-id equals header-demo (or Click Classes contains the agreed token).
  4. GA4 Event tag: event name cta_click; parameters cta_id, cta_location=header, page_path.
  5. Preview: one click → one event; navigate away and back → still one per click, not bursts.
  6. Register cta_id / cta_location as custom dimensions if you will report on them.
  7. Do not import cta_click into Google Ads as a primary conversion.

Then repeat for pricing and footer with new cta_id values — same event name, different parameters. That keeps Explorations clean.

17Form success decision tree

Submit form in Preview
├─ Form event appears?
│  ├─ Yes → does it fire on validation errors too?
│  │  ├─ Yes → do not use for generate_lead; use success-only signal
│  │  └─ No → Form Submission trigger may work for analytics (still prefer DL for money)
│  └─ No → JS/XHR form
│     ├─ Thank-you URL changes? → Page Path trigger (fragile) or better DL
│     ├─ Success toast in DOM? → Element Visibility (once per page)
│     └─ Best → dataLayer.push({event:'generate_lead',...}) on backend success

Print this tree in the engineering ticket so debates end faster.

18Measuring content downloads without vanity

Gate PDFs carefully. A file_download event is useful for content ops; it is rarely a sales conversion. Send file_name and file_extension parameters. If the PDF sits behind a form, the conversion is still generate_lead — the download is a subsequent step. Mixing them in Ads teaches the algorithm to optimise for brochure collectors.

19Try it yourself

Disable any scroll or click tags that duplicate enhanced measurement. Keep one intentional custom interaction event.

20Quick quiz

1. Why might Form Submission triggers miss HubSpot embeds?

Answer: Many embeds submit via JavaScript without a native submit event.

2. Should scroll depth be a Google Ads primary conversion?

Answer: No. It is an engagement signal, not a business outcome.

3. What attribute helps durable CTA tracking?

Answer: A stable data-cta (or similar) hook read via Auto-Event Variable.

21What should you check before the next lesson?

  • Enhanced measurement reviewed.
  • One clean CTA event OR a documented reason you skipped it.
  • Form approach decided (native vs data layer).

Previous: Consent Mode v2 and CMPs. Next: Ecommerce data layer. Or return to the GTM for Marketers track.

22Common questions

Track every click? No — only decision-useful CTAs.

Enhanced measurement for scrolls? Often enough; customise when you need thresholds.

Form trigger silent? Use data layer or thank-you; verify in Preview.

Questions people ask

3 answers

Should I track every click?

No. Track CTAs and paths that inform decisions. High-volume click spam makes analysis worse.

Is enhanced measurement enough for scrolls?

Often yes for 'reached bottom'. Use GTM Element Visibility or Scroll Depth when you need custom thresholds.

Form submit trigger not firing?

Common with JS/XHR forms. Use data layer success events or thank-you page views instead.

Sources

  1. Google Tag Manager Help, Configure Google tag settingssupport.google.com
  2. Google Tag Manager Help, Set up Google Analytics eventssupport.google.com
  3. All accessed 5 Nov 2026.
Written and reviewed by Dipuck JonesHow we research and testReport an error