79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
/**
|
|
* Scroll Tracking Hook
|
|
* Tracks user scroll depth and sends events to Google Tag Manager
|
|
*/
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import { trackScrollDepth } from '../services/gtm';
|
|
|
|
/**
|
|
* Custom hook to track scroll depth for GTM analytics
|
|
*
|
|
* Monitors user scroll behavior and triggers GTM events when the user reaches
|
|
* specific depth milestones (25%, 50%, 75%, 90%, 100%). Each milestone is
|
|
* tracked only once per page visit to avoid duplicate events.
|
|
*
|
|
* Features:
|
|
* - Debounced scroll event handling (100ms) for performance
|
|
* - Tracks depth milestones: 25%, 50%, 75%, 90%, 100%
|
|
* - Prevents duplicate tracking of the same milestone
|
|
* - Automatically resets tracking state on route changes
|
|
* - Checks initial scroll position on mount
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* function MyPage() {
|
|
* useScrollTracking();
|
|
* return <div>...</div>;
|
|
* }
|
|
* ```
|
|
*
|
|
* @remarks
|
|
* This hook should be used at the page/route level component to ensure
|
|
* accurate tracking across navigation. The tracking state is automatically
|
|
* reset when the URL pathname changes.
|
|
*/
|
|
export const useScrollTracking = () => {
|
|
const trackedDepths = useRef(new Set<number>());
|
|
|
|
useEffect(() => {
|
|
let scrollTimeout: NodeJS.Timeout;
|
|
const depths = [25, 50, 75, 90, 100];
|
|
|
|
const handleScroll = () => {
|
|
clearTimeout(scrollTimeout);
|
|
|
|
scrollTimeout = setTimeout(() => {
|
|
const windowHeight = window.innerHeight;
|
|
const documentHeight = document.documentElement.scrollHeight;
|
|
const scrollTop = window.scrollY;
|
|
const scrollPercentage = Math.round(
|
|
((scrollTop + windowHeight) / documentHeight) * 100
|
|
);
|
|
|
|
// Track each depth milestone only once
|
|
depths.forEach(depth => {
|
|
if (scrollPercentage >= depth && !trackedDepths.current.has(depth)) {
|
|
trackedDepths.current.add(depth);
|
|
trackScrollDepth(depth);
|
|
}
|
|
});
|
|
}, 100); // Debounce scroll events
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll);
|
|
|
|
// Check initial scroll position
|
|
handleScroll();
|
|
|
|
return () => {
|
|
window.removeEventListener('scroll', handleScroll);
|
|
clearTimeout(scrollTimeout);
|
|
};
|
|
}, []);
|
|
|
|
// Reset tracked depths when route changes
|
|
useEffect(() => {
|
|
trackedDepths.current.clear();
|
|
}, [window.location.pathname]);
|
|
}; |