Membership Required
You need to sign in and have a Premium subscription to access this content.
- 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
+ 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.