A page was left. Carries visible time and how far down the reader got.
| Field | Value |
|---|---|
| Name | page_leave |
| Severity | 9 (INFO) |
| Written by | Browser tag. |
| Turned off by | data-track-leave="false" |
Once per page view, at whichever comes first of the tab being hidden, the page unloading, and a client-side navigation to a different path.
Once, not twice. visibilitychange and pagehide both fire on the way out of a visit, and a second entry would double the denominator of every average built on this one.
A single-page app gets one before each new page_view, so time on page describes every route rather than only the last one somebody happened to be standing on.
| Attribute | Type | What it holds |
|---|---|---|
| firstrun.duration_ms | number | Visible milliseconds. The clock stops while the tab is hidden and starts again when it comes back. |
| firstrun.scroll_pct | number | 0 to 100. The deepest point reached, not where they happened to be at the end. |
Every entry also carries the resource its client sends once per batch: session.id, user.id once you have called user(), and the rest of the list on the overview. Anything you pass yourself lands in the same map, and your key wins on a collision.
No url. The entry says how long and how far, not where, so a page_leave cannot be grouped by page on its own. Its session.id is the join back to the page_view that preceded it.
The clock is visible time and not wall clock. A tab left open behind twelve others for an hour did not hold anyone's attention for an hour, and an average that says it did is worse than no average. There is no mouse track, no heatmap and no per-element dwell.
A widget is a filter, a group by, an aggregate, a time bucket and a limit. These are those five parts written out, and every one of them is something you can build yourself.
| Question | The query |
|---|---|
| Average time on page | Name is page_leave . average of firstrun.duration_ms |
| How far people scroll | Name is page_leave . average of firstrun.scroll_pct |
| Whether engagement is moving | Name is page_leave . average of firstrun.duration_ms . bucket by day |
| The slow tail | Name is page_leave . 90th percentile of firstrun.duration_ms |