Skip to main content
6 min read

Scroll Depth

Zenovay records how far each visitor scrolls on every pageview. Three surfaces in the dashboard turn that signal into something you can act on:

  1. Pages tab — Scroll depth funnel showing, for each page, a small 5-bar "falling staircase" chart whose bar heights are the % of visitors who reached 25 / 50 / 75 / 90 / 100% of the page.
  2. Heatmaps view — Scroll mode rendering a gradient overlay on top of your page screenshot.
  3. Session detail — Max scroll depth displayed inline with session time and pages viewed.

What's tracked

On every pageview, the tracker measures (scrollTop + viewportHeight) / documentHeight × 100 continuously as the visitor scrolls. The deepest value reached during the session is stored against the visitor record (visitors.scroll_depth_percentage, 0-100 integer). The page's total height is recomputed on every scroll event, so dynamic content (infinite scroll, accordions, lazy-loaded images) is handled naturally.

Separately, the tracker emits fire-once milestone events at the five plan-spec thresholds:

MilestoneFires whenStored as
25%First time scroll_percent >= 25event_type='scroll_milestone', event_data.depth=25
50%First time scroll_percent >= 50event_type='scroll_milestone', event_data.depth=50
75%First time scroll_percent >= 75depth=75
90%First time scroll_percent >= 90depth=90
100%First time scroll_percent >= 100depth=100

Once a milestone fires for a session, it does not fire again — scrolling back up and forward again is a no-op. On SPA route changes (pushState / replaceState / popstate), the milestone-reached set is reset so the new route starts fresh.

Scroll-depth tracking is part of the heatmaps feature, available on the Pro plan and above. Free-tier websites don't record scroll events.

Pages-tab funnel chart

Open any domain dashboard → Pages tab. Beneath the Pages / Location / System cards, the full-width Scroll depth funnel widget shows your top 10 pages. Each row is one page with a small 5-bar mini-chart in the centre. The bars are baseline-aligned and use a single accent colour — their heights encode each milestone's reach:

  • 25% — height ∝ % of pageviews that reached at least a quarter of the page
  • 50% — half
  • 75% — three quarters
  • 90% — nearly the bottom
  • 100% — the very bottom (see Practical-100% tolerance below)

Because scroll depth is monotonic (the share that reached 50% is always ≤ the share that reached 25%), the bars naturally descend left-to-right. The shape itself — a falling staircase — is the visual signal: long staircases mean visitors are reading deep, short ones mean drop-off near the top.

The percentages are computed against visitors.scroll_depth_percentage for that page over the selected period. Each row's milestone %s are always visible beneath the bars — no hover needed. Pages with fewer than 10 pageviews (or with effectively zero scroll engagement) are marked low signal and dimmed so they don't dominate the view.

Subdomain disambiguation

If your tracking code is used across multiple subdomains (e.g. both zenovay.com and docs.zenovay.com point to the same tracker), each subdomain × path combination gets its own row. The hostname appears as a muted prefix before the bold path, so you can tell zenovay.com/en/ apart from docs.zenovay.com/en/ at a glance — useful when the same path serves different content on different subdomains.

The default view shows the top 5 pages; a Show all button reveals the rest inside a fixed-height scroll area, keeping the card the same height as its neighbours.

Practical-100% tolerance

The tracker computes scroll percentage as (scrollTop + viewportHeight) / documentHeight × 100. At the visual bottom, subpixel rendering and mobile address-bar resizing mean the math often tops out at 99.7-99.9% rather than exactly 100. The tracker therefore snaps to 100 when the viewport bottom is within 2 px of the document bottom, and Zenovay's dashboards count scroll_depth_percentage ≥ 95 as "reached 100%". Historical data captured before the snap-to-100 fix benefits from the same tolerance retroactively, so the "100% reached" column shows real engagement instead of being structurally zero.

How to read it

PatternLikely causeAction
80% / 45% / 18% / 6% / 0%Healthy funnel; long-form content read partiallyOptimise the content above the 50% mark — it's seen by most readers
95% / 90% / 80% / 70% / 50%Very short page or strong reader engagementCheck whether the bottom CTA actually loads in the viewport
30% / 5% / 1% / 0% / 0%Visitors bouncing at the foldInvestigate above-the-fold copy, load time, or layout shift

A drop from 19% (75%) to 6% (90%) on a sales page is the most common diagnostic signal — something at the bottom is losing readers before they hit the CTA.

Scroll heatmap overlay

In the Heatmaps view, the segmented control in the header lets you switch between Click and Scroll modes. Scroll mode renders a gradient overlay on the page screenshot. The colour at each height encodes the percentage of visitors who reached that point.

Because the top of every page is reached by everyone, the top of the screenshot is always the hottest colour. The gradient fades as the percentage drops — a sharp transition is a "fall-off point" and usually maps to a specific design element (the end of the hero section, a layout shift, a heavy-ad block).

The scroll heatmap requires the page screenshot to be captured. Screenshots are automatically captured on the first heatmap-enabled pageview per page; allow a few minutes after enabling heatmaps for the first screenshot to appear.

Session detail max scroll

When you open a session from the live globe or visitor list, the side panel shows Max scroll depth: X% next to Pages viewed and Session time. This is the deepest scroll the visitor reached across all pages in that session.

A visitor showing 12% max scroll on a long-form page indicates a probable bounce. A visitor showing 100% scroll on a long page is highly engaged regardless of session time — they read the whole thing.

Scroll-depth tracking lives entirely inside the tracker's in-memory state per page load. Nothing is written to cookies or localStorage, so the feature is unaffected by cookie consent banners. On websites running with Sec-GPC (Global Privacy Control), the milestone-event ingest path is skipped server-side — only the visitor-level max scroll is retained, exactly the same data Zenovay records for non-GPC traffic.

Was this page helpful?