Skip to content

Instantly share code, notes, and snippets.

@ceaksan
Created April 17, 2026 20:39
Show Gist options
  • Select an option

  • Save ceaksan/6d9db87a225cb5595dd5491d68df52aa to your computer and use it in GitHub Desktop.

Select an option

Save ceaksan/6d9db87a225cb5595dd5491d68df52aa to your computer and use it in GitHub Desktop.
Cart Abandonment Beacon Client - navigator.sendBeacon heartbeat client for server-side cart abandonment detection.
/**
* Cart Abandonment - Beacon Client
*
* Server-side cart abandonment detector (client half). Emits beacons
* for page_enter, periodic heartbeat, tab_hidden, tab_visible and
* purchase_complete events so that a backend can decide abandonment
* based on heartbeat silence (e.g. 30 minutes without heartbeat +
* no purchase => abandonment).
*
* Pairs with the server-side handler:
* https://gist.github.com/ceaksan/cart_abandonment_beacon_server.py
*
* Install via: GTM Custom HTML tag, trigger = All Pages / DOM Ready.
*
* @see https://ceaksan.com/tr/sepet-terk-takibi-javascript
* @license MIT
*/
(function () {
var ENDPOINT = "https://analytics.yoursite.com/cart-beacon";
var HEARTBEAT_MS = 10000;
var sessionId = sessionStorage.getItem("beacon_session");
if (!sessionId) {
sessionId =
"sess_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
sessionStorage.setItem("beacon_session", sessionId);
}
var path = window.location.pathname;
var isCartOrCheckout = /\/(sepet|cart|odeme|checkout)/i.test(path);
var isOrderSuccess = /\/(siparis-sonuc|thank[-_]?you)/i.test(path);
function sendBeacon(eventType) {
var data = JSON.stringify({
eventType: eventType,
sessionId: sessionId,
url: window.location.href,
timestamp: new Date().toISOString(),
cartValue: extractCartValue(),
});
if (navigator.sendBeacon) {
navigator.sendBeacon(
ENDPOINT,
new Blob([data], { type: "application/json" }),
);
}
}
function extractCartValue() {
if (window.dataLayer) {
for (var i = window.dataLayer.length - 1; i >= 0; i--) {
var item = window.dataLayer[i];
if (item.ecommerce && item.ecommerce.value) {
return item.ecommerce.value;
}
}
}
return null;
}
// Order success: cancel pending abandonment
if (isOrderSuccess) {
sendBeacon("purchase_complete");
return;
}
// Only run on cart and checkout pages
if (!isCartOrCheckout) return;
// Page enter beacon
sendBeacon("page_enter");
// Heartbeat: active signal every 10s
var heartbeatInterval = setInterval(function () {
sendBeacon("heartbeat");
}, HEARTBEAT_MS);
// Tab hidden beacon (mobile-reliable)
document.addEventListener("visibilitychange", function () {
if (document.visibilityState === "hidden") {
sendBeacon("tab_hidden");
clearInterval(heartbeatInterval);
} else if (document.visibilityState === "visible") {
sendBeacon("tab_visible");
heartbeatInterval = setInterval(function () {
sendBeacon("heartbeat");
}, HEARTBEAT_MS);
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment