Skip to content

Commit 4585241

Browse files
authored
feat(telemetry): measure 25%/50%/75% scroll depth (#11041)
GA4 already measures 90%, and we instrument measurements for 25%/50%/75% to better understand how far users scroll.
1 parent 56a8ee5 commit 4585241

File tree

1 file changed

+45
-0
lines changed

1 file changed

+45
-0
lines changed

Diff for: client/src/app.tsx

+45
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { TopPlacement } from "./ui/organisms/placement";
3535
import { Blog } from "./blog";
3636
import { Newsletter } from "./newsletter";
3737
import { Curriculum } from "./curriculum";
38+
import { useGA } from "./ga-context";
3839

3940
const AllFlaws = React.lazy(() => import("./flaws"));
4041
const Translations = React.lazy(() => import("./translations"));
@@ -135,6 +136,7 @@ export function App(appProps: HydrationData) {
135136

136137
usePing();
137138
useGleanPage(pageNotFound, appProps.doc);
139+
useScrollDepthMeasurement();
138140

139141
const localeMatch = useMatch("/:locale/*");
140142

@@ -349,3 +351,46 @@ export function App(appProps: HydrationData) {
349351
);
350352
return routes;
351353
}
354+
355+
function useScrollDepthMeasurement(thresholds = [25, 50, 75]) {
356+
const timeoutID = React.useRef<number | null>();
357+
const [currentDepth, setScrollDepth] = React.useState(0);
358+
const { gtag } = useGA();
359+
360+
useEffect(() => {
361+
const listener = () => {
362+
if (timeoutID.current) {
363+
window.clearTimeout(timeoutID.current);
364+
}
365+
timeoutID.current = window.setTimeout(() => {
366+
const { scrollHeight } = document.documentElement;
367+
const { innerHeight, scrollY } = window;
368+
const scrollPosition = innerHeight + scrollY;
369+
const depth = (100 * scrollPosition) / scrollHeight;
370+
371+
const matchingThresholds = thresholds.filter(
372+
(threshold) => currentDepth < threshold && threshold <= depth
373+
);
374+
375+
matchingThresholds.forEach((threshold) => {
376+
gtag("event", "scroll", {
377+
percent_scrolled: String(threshold),
378+
});
379+
});
380+
381+
const lastThreshold = matchingThresholds.at(-1);
382+
if (lastThreshold) {
383+
setScrollDepth(lastThreshold);
384+
}
385+
386+
timeoutID.current = null;
387+
}, 100);
388+
};
389+
390+
window.addEventListener("scroll", listener);
391+
392+
return () => window.removeEventListener("scroll", listener);
393+
});
394+
395+
return currentDepth;
396+
}

0 commit comments

Comments
 (0)