Skip to content
ceaksan
PREMIUM web-analytics

Scroll Depth Tracking: Measuring Content Engagement Beyond GA4 and GTM

Why GA4's 90% scroll event and GTM's Scroll Depth trigger fall short. Content-scoped scroll tracking with ScrollTracker: engaged, scanned, and skipped classification using dwell time, velocity, and dynamic height versioning.

Feb 11, 2026 Updated: Mar 29, 2026
TL;DR

GA4 fires a single scroll event only at the 90% threshold. GTM's Scroll Depth trigger supports multiple thresholds but measures full page height, provides no engagement quality info, and breaks with dynamic content. ScrollTracker uses content-scoped measurement, dwell time, and velocity classification to distinguish whether users actually read the content.

Membership Required

You need to sign in and have a Premium subscription to access this content.

Key Takeaways
  • 01 GA4 Enhanced Measurement fires a single binary scroll event only at the 90% threshold, missing all intermediate depths
  • 02 GTM's Scroll Depth trigger measures full page height including header, footer, and sidebar. If the content area is 60% of the page, the 100% scroll threshold never fires
  • 03 Scroll depth alone is not an engagement indicator: there is no difference between fast scrolling and careful reading
  • 04 Content-scoped measurement tracks only the target content area, providing consistent results regardless of page layout
  • 05 Dwell time and velocity classification distinguish engaged (read), scanned (skimmed), and skipped (passed over)
  • 06 The height_version system handles dynamic content changes from lazy loading, accordions, and infinite scroll
  • 07 The visibilitychange API pauses dwell timers during tab switches, preventing false engagement data
Frequently Asked Questions (FAQ)
+ Why do I need a custom solution when GA4 has a built-in scroll event?

GA4 Enhanced Measurement fires a single scroll event only when the user reaches 90% of the page. You cannot see whether users leave at 25%, 50%, or 75%. It also measures full page height including header, footer, and sidebar. If your content area is 60% of the page, the 100% scroll threshold never fires.

+ GTM's Scroll Depth trigger supports multiple thresholds. Isn't that enough?

GTM's Scroll Depth trigger supports multiple thresholds but has three fundamental issues: it measures full page height (not content-scoped), it cannot distinguish between fast scrolling and slow reading, and thresholds shift when page height changes (lazy load, accordion).

+ What is the difference between engaged, scanned, and skipped?

Engaged: the user stayed in that zone long enough and scrolled slowly, reading the content. Scanned: the user passed through at moderate speed, skimming the content. Skipped: the user passed through too quickly or stayed too briefly, ignoring the content.

+ What does height_version do?

Dynamic content (lazy load, accordion, infinite scroll) changes page height. height_version tracks these changes. In per_height_version reEntry mode, previously passed thresholds become re-fireable when height changes.

+ Can I use this code on SPA (Single Page Application) sites?

Yes. The ScrollTracker.destroy() method cleans up all event listeners and the ResizeObserver. Call destroy() on page transitions and re-initialize with init() on the new page.

+ Why is visibilitychange important?

When the user switches tabs or locks their phone, the dwell timer keeps running. Someone who leaves a tab in the background for 30 minutes would appear as super engaged. The visibilitychange API pauses timers when the tab is hidden and resumes them when shown.

+ How can I use these events in Looker Studio?

Define scroll_pct, scroll_type, dwell_ms, and height_version as custom dimensions in GA4. In Looker Studio, create an engagement distribution pie chart with scroll_type breakdown, a scroll_pct x avg dwell_ms scatter plot, and a content engagement scorecard by page_type.