feat: add automatic 404 detection, scroll depth tracking, and scroll depth dashboard card
- 404 detection: checks document.title for "404" or "not found", fires custom event, SPA-aware - Scroll depth: passive scroll listener fires events at 25/50/75/100% thresholds - ScrollDepth dashboard card: progress bar visualization showing % of visitors reaching each threshold - Scroll events filtered out of GoalStats to avoid duplication - Both features on by default, opt-out via data-no-404 / data-no-scroll
This commit is contained in:
@@ -299,6 +299,10 @@
|
||||
if (url !== lastUrl) {
|
||||
lastUrl = url;
|
||||
trackPageview();
|
||||
// * Check for 404 after SPA navigation (deferred so title updates first)
|
||||
setTimeout(check404, 100);
|
||||
// * Reset scroll depth tracking for the new page
|
||||
if (trackScroll) scrollFired = {};
|
||||
}
|
||||
}
|
||||
new MutationObserver(onUrlChange).observe(document, { subtree: true, childList: true });
|
||||
@@ -308,7 +312,11 @@
|
||||
history.replaceState = function() { _replace.apply(this, arguments); onUrlChange(); };
|
||||
|
||||
// * Track popstate (browser back/forward)
|
||||
window.addEventListener('popstate', trackPageview);
|
||||
window.addEventListener('popstate', function() {
|
||||
trackPageview();
|
||||
setTimeout(check404, 100);
|
||||
if (trackScroll) scrollFired = {};
|
||||
});
|
||||
|
||||
// * Custom events / goals: validate event name (letters, numbers, underscores only; max 64 chars)
|
||||
var EVENT_NAME_MAX = 64;
|
||||
@@ -346,6 +354,62 @@
|
||||
window.pulse = window.pulse || {};
|
||||
window.pulse.track = trackCustomEvent;
|
||||
|
||||
// * Auto-track 404 error pages (on by default)
|
||||
// * Detects pages where document.title contains "404" or "not found"
|
||||
// * Opt-out: add data-no-404 to the script tag
|
||||
var track404 = !script.hasAttribute('data-no-404');
|
||||
var sent404ForUrl = '';
|
||||
|
||||
function check404() {
|
||||
if (!track404) return;
|
||||
// * Only fire once per URL
|
||||
var currentUrl = location.href;
|
||||
if (sent404ForUrl === currentUrl) return;
|
||||
if (/404|not found/i.test(document.title)) {
|
||||
sent404ForUrl = currentUrl;
|
||||
trackCustomEvent('404');
|
||||
}
|
||||
}
|
||||
|
||||
// * Check on initial load (deferred so SPAs can set title)
|
||||
setTimeout(check404, 0);
|
||||
|
||||
// * Auto-track scroll depth at 25%, 50%, 75%, and 100% (on by default)
|
||||
// * Each threshold fires once per pageview; resets on SPA navigation
|
||||
// * Opt-out: add data-no-scroll to the script tag
|
||||
var trackScroll = !script.hasAttribute('data-no-scroll');
|
||||
|
||||
if (trackScroll) {
|
||||
var scrollThresholds = [25, 50, 75, 100];
|
||||
var scrollFired = {};
|
||||
var scrollTicking = false;
|
||||
|
||||
function checkScroll() {
|
||||
var docHeight = document.documentElement.scrollHeight;
|
||||
var viewHeight = window.innerHeight;
|
||||
// * Page fits in viewport — nothing to scroll
|
||||
if (docHeight <= viewHeight) return;
|
||||
var scrollTop = window.scrollY;
|
||||
var scrollPercent = Math.round((scrollTop + viewHeight) / docHeight * 100);
|
||||
|
||||
for (var i = 0; i < scrollThresholds.length; i++) {
|
||||
var t = scrollThresholds[i];
|
||||
if (!scrollFired[t] && scrollPercent >= t) {
|
||||
scrollFired[t] = true;
|
||||
trackCustomEvent('scroll_' + t);
|
||||
}
|
||||
}
|
||||
scrollTicking = false;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!scrollTicking) {
|
||||
scrollTicking = true;
|
||||
requestAnimationFrame(checkScroll);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// * Auto-track outbound link clicks and file downloads (on by default)
|
||||
// * Opt-out: add data-no-outbound or data-no-downloads to the script tag
|
||||
var trackOutbound = !script.hasAttribute('data-no-outbound');
|
||||
|
||||
Reference in New Issue
Block a user