Cart timer script
August 26, 2025 · AI Conversations
– starts a **20-minute cart timer** every time someone lands on `buy.php`
– shows how to **update a countdown** (optional)
– **clears localStorage (your cart keys)** the moment 20 min elapse
– also clears on **re-open** after 20 min or across **multiple tabs** (via `storage` event)
<script>
/**
* Cart Timer (20 minutes) for buy.php
* - Creates a deadline on first visit (persists in localStorage).
* - Clears cart/localStorage immediately when 20 minutes pass.
* - Works across browser close/reopen and across tabs.
*/
(function () {
// Only run on buy.php (safety guard if this script is global)
if (!//buy.php(?|$)/i.test(location.pathname + location.search)) return;
// ---- CONFIG ----
const DURATION_MS = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = 'fos_cart_deadline_ts';
// Add your cart/localStorage keys here:
const CART_KEYS = [
'orderData',
'cart',
'tickets',
'addOns',
'selectedEventDateString',
// add any other keys you use…
];
// OPTIONAL: Provide a selector to show countdown text (e.g., "#cart-timer")
const COUNTDOWN_EL_SELECTOR = '#cart-timer';
// ---- HELPERS ----
function now() { return Date.now(); }
function readDeadline() {
const v = localStorage.getItem(DEADLINE_KEY);
const n = v ? parseInt(v, 10) : NaN;
return Number.isFinite(n) ? n : null;
}
function writeDeadline(ts) {
localStorage.setItem(DEADLINE_KEY, String(ts));
}
function clearCartAndDeadline(reason = '') {
try {
// Clear your cart-related keys only (safer than wiping all localStorage)
CART_KEYS.forEach(k => localStorage.removeItem(k));
localStorage.removeItem(DEADLINE_KEY);
// If you really want to nuke ALL localStorage on this origin, uncomment:
// localStorage.clear();
// Also broadcast a signal for other tabs (optional)
localStorage.setItem('fos_cart_last_clear', String(Date.now()));
console.log('[CartTimer] Cleared cart due to:', reason || 'deadline reached');
} catch (e) {
console.warn('[CartTimer] Clear error:', e);
}
}
function ensureDeadline() {
let dl = readDeadline();
if (!dl) {
dl = now() + DURATION_MS;
writeDeadline(dl);
console.log('[CartTimer] New deadline set:', new Date(dl).toISOString());
}
return dl;
}
function timeLeft(deadlineTs) {
return Math.max(0, deadlineTs - now());
}
function scheduleAutoClear(deadlineTs) {
const ms = timeLeft(deadlineTs);
if (ms <= 0) {
clearCartAndDeadline('expired on load');
return;
}
// Clear right at the deadline
window.setTimeout(() => {
// Double-check in case user extended or page clock drifted
const latest = readDeadline();
if (latest && now() >= latest) {
clearCartAndDeadline('expired on timeout');
// (Optionally) refresh page or redirect:
// location.reload();
}
}, ms);
}
// OPTIONAL: Simple countdown UI updater (MM:SS)
function startCountdown(deadlineTs) {
const el = document.querySelector(COUNTDOWN_EL_SELECTOR);
if (!el) return;
const tick = () => {
const left = timeLeft(deadlineTs);
const mm = Math.floor(left / 60000);
const ss = Math.floor((left % 60000) / 1000);
el.textContent = (mm < 10 ? '0' : '') + mm + ':' + (ss < 10 ? '0' : '') + ss;
if (left <= 0) {
// Force clear + update UI once more
clearCartAndDeadline('expired on countdown');
el.textContent = '00:00';
// location.reload(); // optional
return;
}
requestAnimationFrame(() => setTimeout(tick, 250)); // smooth-ish update without hammering
};
tick();
}
// Keep tabs in sync: if another tab clears, reflect here
window.addEventListener('storage', (e) => {
// If deadline changed/removed or we received a clear signal
if (e.key === DEADLINE_KEY || e.key === 'fos_cart_last_clear') {
const dl = readDeadline();
if (!dl || now() >= dl) {
clearCartAndDeadline('sync from other tab');
// location.reload(); // optional
}
}
});
// ---- RUN ----
const deadline = ensureDeadline();
scheduleAutoClear(deadline);
startCountdown(deadline);
})();
</script>
### How it works (quick notes)
– On first visit to `buy.php`, it creates and stores a **deadline timestamp** in `localStorage`.
– Every load checks the timestamp.
– If **already expired** (e.g., you closed the browser for >20 min), it **immediately clears** your cart keys.
– If **not expired**, it sets a `setTimeout` to clear exactly at the deadline.
– A **`storage`** event listener keeps multiple tabs/windows in sync.
– You control **which keys** to clear via `CART_KEYS`.
If you also need to clear a **server-side cart/session**, call your API inside `clearCartAndDeadline()` as well.
<div id="timer"></div>
<script>
// 20 minutes in milliseconds
const DURATION = 20 * 60 * 1000;
// Get deadline (20 min from now)
const deadline = Date.now() + DURATION;
function updateTimer() {
const now = Date.now();
let remaining = deadline - now;
if (remaining <= 0) {
document.getElementById("timer").textContent = "00:00";
clearInterval(timerInterval);
alert("⏰ Time's up!"); // action after 20 min
return;
}
const minutes = Math.floor(remaining / 1000 / 60);
const seconds = Math.floor((remaining / 1000) % 60);
document.getElementById("timer").textContent =
`${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
}
// update immediately + then every second
updateTimer();
const timerInterval = setInterval(updateTimer, 1000);
</script>
✅ This will show a countdown like **19:59 → 00:00**.
✅ After 20 minutes it runs whatever logic you put in place of `alert(…)`.
Do you want me to extend this so it **persists even if the user closes and reopens the browser** (using `localStorage`), or keep it as a simple on-page timer?
<div class="timer"></div>
<div class="timer"></div>
<div class="timer"></div>
<script>
// 20 minutes in milliseconds
const DURATION = 20 * 60 * 1000;
// Create a single deadline for all timers
const deadline = Date.now() + DURATION;
function updateTimers() {
const now = Date.now();
let remaining = deadline - now;
if (remaining <= 0) {
document.querySelectorAll(".timer").forEach(el => {
el.textContent = "00:00";
});
clearInterval(timerInterval);
alert("⏰ Time's up!");
return;
}
const minutes = Math.floor(remaining / 1000 / 60);
const seconds = Math.floor((remaining / 1000) % 60);
const formatted = `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
document.querySelectorAll(".timer").forEach(el => {
el.textContent = formatted;
});
}
// Update immediately + every second
updateTimers();
const timerInterval = setInterval(updateTimers, 1000);
</script>
👉 Now every `.timer` element on the page will stay in sync and display the same countdown.
Do you also want me to make this **persist across browser refresh/close** (so if the user reopens `buy.php`, it continues from where it left), or just keep this simple session-only version?
That way, if the user refreshes or closes/reopens the page, the timer continues from where it left, and when time’s up, all `.timer` elements show `00:00`.
Here’s the code:
<div class="timer"></div>
<div class="timer"></div>
<script>
(function () {
const DURATION = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = "cart_deadline"; // localStorage key
// Get deadline from localStorage or set new
let deadline = localStorage.getItem(DEADLINE_KEY);
if (!deadline) {
deadline = Date.now() + DURATION;
localStorage.setItem(DEADLINE_KEY, deadline);
} else {
deadline = parseInt(deadline, 10);
}
function updateTimers() {
const now = Date.now();
let remaining = deadline - now;
if (remaining <= 0) {
document.querySelectorAll(".timer").forEach(el => {
el.textContent = "00:00";
});
clearInterval(timerInterval);
// Clear storage after time up
localStorage.removeItem(DEADLINE_KEY);
alert("⏰ Time's up!");
return;
}
const minutes = Math.floor(remaining / 1000 / 60);
const seconds = Math.floor((remaining / 1000) % 60);
const formatted =
`${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
document.querySelectorAll(".timer").forEach(el => {
el.textContent = formatted;
});
}
// Start ticking
updateTimers();
const timerInterval = setInterval(updateTimers, 1000);
})();
</script>
✅ On first visit, it sets a deadline (now + 20 min).
✅ On refresh/reopen, it reads the same deadline and resumes countdown.
✅ Once 20 min passes, it clears the deadline from `localStorage`, shows `00:00`, and you can also clear cart keys there.
Do you want me to also plug in the **cart clear logic** right inside this timer (so localStorage cart data is automatically wiped at expiry)?
also I want to clear fossettsOrderData once 20 min complete and order not procssed give me updated code only I will relace
`fossettsOrderData` after 20 minutes (if order not processed):
<script>
(function () {
const DURATION = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = "cart_deadline"; // deadline key in storage
const ORDER_KEY = "fossettsOrderData"; // your order data key
// Get deadline from localStorage or set new
let deadline = localStorage.getItem(DEADLINE_KEY);
if (!deadline) {
deadline = Date.now() + DURATION;
localStorage.setItem(DEADLINE_KEY, deadline);
} else {
deadline = parseInt(deadline, 10);
}
function updateTimers() {
const now = Date.now();
let remaining = deadline - now;
if (remaining <= 0) {
document.querySelectorAll(".timer").forEach(el => {
el.textContent = "00:00";
});
clearInterval(timerInterval);
// Clear deadline + order data
localStorage.removeItem(DEADLINE_KEY);
localStorage.removeItem(ORDER_KEY);
console.log("⏰ Time's up — fossettsOrderData cleared");
return;
}
const minutes = Math.floor(remaining / 1000 / 60);
const seconds = Math.floor((remaining / 1000) % 60);
const formatted =
`${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
document.querySelectorAll(".timer").forEach(el => {
el.textContent = formatted;
});
}
// Start ticking
updateTimers();
const timerInterval = setInterval(updateTimers, 1000);
})();
</script>
👉 Just replace your old timer block with this one.
This will **wipe `fossettsOrderData`** from `localStorage` when 20 minutes expire.
Do you also want me to add a safeguard so that if the user **closes the browser and comes back after 20+ minutes**, it immediately clears `fossettsOrderData` on page load (not just after ticking)?
<script>
(function () {
const DURATION = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = "cart_deadline"; // deadline key
const ORDER_KEY = "fossettsOrderData"; // order data key
// Get deadline from localStorage or set new
let deadline = localStorage.getItem(DEADLINE_KEY);
if (!deadline) {
deadline = Date.now() + DURATION;
localStorage.setItem(DEADLINE_KEY, deadline);
} else {
deadline = parseInt(deadline, 10);
}
function updateTimers() {
const now = Date.now();
let remaining = deadline - now;
if (remaining <= 0) {
document.querySelectorAll(".timer").forEach(el => {
el.textContent = "00:00";
});
clearInterval(timerInterval);
// Clear deadline + order data
localStorage.removeItem(DEADLINE_KEY);
localStorage.removeItem(ORDER_KEY);
console.log("⏰ Time's up — cleared fossettsOrderData and redirecting…");
// Redirect to home page
window.location.href = "/";
return;
}
const minutes = Math.floor(remaining / 1000 / 60);
const seconds = Math.floor((remaining / 1000) % 60);
const formatted =
`${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
document.querySelectorAll(".timer").forEach(el => {
el.textContent = formatted;
});
}
// Start ticking
updateTimers();
const timerInterval = setInterval(updateTimers, 1000);
})();
</script>
✅ On first visit, timer starts (20 min).
✅ If user refreshes, it continues from where it left.
✅ When 20 minutes pass:
– `fossettsOrderData` is cleared
– deadline is removed
– user is **redirected to the home page `/`**
Do you want the redirect to happen **instantly at 20 minutes**, or should I add a short delay (like 2–3 seconds) after clearing to let the user see `"00:00"`?
Option to refresh and start over
<script>
(function () {
const DURATION = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = "cart_deadline";
const ORDER_KEY = "fossettsOrderData";
// --- UI: show "Cart expired" with restart option ---
function showExpiryUI() {
// Set all visible timers to 00:00
document.querySelectorAll(".timer").forEach(el => (el.textContent = "00:00"));
// Create a lightweight overlay/banner
const wrap = document.createElement("div");
wrap.setAttribute("id", "cart-expired-wrap");
wrap.style.position = "fixed";
wrap.style.inset = "0";
wrap.style.background = "rgba(0,0,0,0.55)";
wrap.style.display = "flex";
wrap.style.alignItems = "center";
wrap.style.justifyContent = "center";
wrap.style.zIndex = "9999";
const box = document.createElement("div");
box.style.background = "#fff";
box.style.padding = "20px 24px";
box.style.borderRadius = "12px";
box.style.maxWidth = "420px";
box.style.width = "92%";
box.style.textAlign = "center";
box.style.boxShadow = "0 10px 30px rgba(0,0,0,0.2)";
const title = document.createElement("div");
title.textContent = "Cart expired";
title.style.fontSize = "20px";
title.style.fontWeight = "700";
title.style.marginBottom = "8px";
const msg = document.createElement("div");
msg.textContent = "Your 20-minute hold has ended. Refresh to start over.";
msg.style.fontSize = "14px";
msg.style.color = "#444";
msg.style.marginBottom = "16px";
const btn = document.createElement("button");
btn.textContent = "Refresh & start over";
btn.style.padding = "10px 16px";
btn.style.border = "none";
btn.style.borderRadius = "8px";
btn.style.cursor = "pointer";
btn.style.fontWeight = "600";
btn.style.fontSize = "14px";
btn.style.background = "#111";
btn.style.color = "#fff";
btn.onclick = function () {
// on reload, a new deadline will be created
window.location.reload();
};
box.appendChild(title);
box.appendChild(msg);
box.appendChild(btn);
wrap.appendChild(box);
document.body.appendChild(wrap);
}
function clearExpiredCart() {
try {
localStorage.removeItem(DEADLINE_KEY);
localStorage.removeItem(ORDER_KEY);
} catch (e) {
console.warn("Failed to clear storage:", e);
}
}
function expireNow() {
clearExpiredCart();
showExpiryUI();
}
// --- Deadline bootstrapping (persistent across refresh) ---
let deadline = parseInt(localStorage.getItem(DEADLINE_KEY) || "", 10);
const nowTs = Date.now();
if (!Number.isFinite(deadline)) {
// First visit (no deadline yet)
deadline = nowTs + DURATION;
localStorage.setItem(DEADLINE_KEY, String(deadline));
}
// If already expired (e.g., user came back after >20 min), expire immediately
if (nowTs >= deadline) {
expireNow();
return;
}
// --- Timer tick for all .timer elements ---
function updateTimers() {
const now = Date.now();
const remaining = Math.max(0, deadline - now);
if (remaining === 0) {
clearInterval(tick);
expireNow();
return;
}
const minutes = Math.floor(remaining / 60000);
const seconds = Math.floor((remaining % 60000) / 1000);
const formatted =
`${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
document.querySelectorAll(".timer").forEach(el => (el.textContent = formatted));
}
// Kick off the ticking loop
updateTimers();
const tick = setInterval(updateTimers, 1000);
})();
</script>
<script>
(function () {
// Run only on buy.php
if (!//buy.php(?|$)/i.test(location.pathname + location.search)) return;
const DURATION = 20 * 60 * 1000; // 20 minutes
const DEADLINE_KEY = "cart_deadline";
const ORDER_KEY = "fossettsOrderData";
const CLEAR_SIGNAL = "fos_cart_last_clear"; // broadcasts to other tabs
function showExpiryUI() {
document.querySelectorAll(".timer").forEach(el => (el.textContent = "00:00"));
const wrap = document.createElement("div");
wrap.id = "cart-expired-wrap";
Object.assign(wrap.style, {
position: "fixed", inset: "0", background: "rgba(0,0,0,0.55)",
display: "flex", alignItems: "center", justifyContent: "center", zIndex: "9999"
});
const box = document.createElement("div");
Object.assign(box.style, {
background: "#fff", padding: "20px 24px", borderRadius: "12px",
maxWidth: "420px", width: "92%", textAlign: "center",
boxShadow: "0 10px 30px rgba(0,0,0,0.2)"
});
const title = document.createElement("div");
title.textContent = "Cart expired";
Object.assign(title.style, { fontSize: "20px", fontWeight: "700", marginBottom: "8px" });
const msg = document.createElement("div");
msg.textContent = "Your 20-minute hold has ended. Refresh to start over.";
Object.assign(msg.style, { fontSize: "14px", color: "#444", marginBottom: "16px" });
const btn = document.createElement("button");
btn.textContent = "Refresh & start over";
Object.assign(btn.style, {
padding: "10px 16px", border: "none", borderRadius: "8px",
cursor: "pointer", fontWeight: "600", fontSize: "14px",
background: "#111", color: "#fff"
});
btn.onclick = function () { window.location.reload(); };
box.appendChild(title);
box.appendChild(msg);
box.appendChild(btn);
wrap.appendChild(box);
document.body.appendChild(wrap);
}
function clearExpiredCart() {
try {
localStorage.removeItem(DEADLINE_KEY);
localStorage.removeItem(ORDER_KEY);
// notify other tabs
localStorage.setItem(CLEAR_SIGNAL, String(Date.now()));
} catch (e) {
console.warn("Failed to clear storage:", e);
}
}
function expireNow() {
clearExpiredCart();
showExpiryUI();
}
// Bootstrap persistent deadline
let deadline = parseInt(localStorage.getItem(DEADLINE_KEY) || "", 10);
const nowTs = Date.now();
if (!Number.isFinite(deadline)) {
// First visit: set a deadline that persists across refresh/close/reopen
deadline = nowTs + DURATION;
localStorage.setItem(DEADLINE_KEY, String(deadline));
} else if (nowTs >= deadline) {
// Already expired while the browser was closed or on another tab
expireNow();
return;
}
function format(remainingMs) {
const m = Math.floor(remainingMs / 60000);
const s = Math.floor((remainingMs % 60000) / 1000);
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}
function render() {
const remaining = Math.max(0, deadline - Date.now());
if (remaining === 0) {
clearInterval(tick);
expireNow();
return;
}
const display = format(remaining);
document.querySelectorAll(".timer").forEach(el => (el.textContent = display));
}
// Start ticking and keep timers in sync
render();
const tick = setInterval(render, 1000);
// Cross-tab sync: if another tab clears or modifies deadline, expire here too
window.addEventListener("storage", (e) => {
if (e.key === DEADLINE_KEY || e.key === CLEAR_SIGNAL) {
const dl = parseInt(localStorage.getItem(DEADLINE_KEY) || "", 10);
if (!Number.isFinite(dl) || Date.now() >= dl) {
clearInterval(tick);
expireNow();
}
}
});
})();
</script>