<script>
(function () {
const SELECTORS = {
wrapper: '[data-exit-popup]',
closes: '[data-popup-close]'
};
const wrapper = document.querySelector(SELECTORS.wrapper);
if (!wrapper) return;
const closeEls = wrapper.querySelectorAll(SELECTORS.closes);
// Session flags
const SEEN_KEY = 'exit_popup_shown'; // set only after close/submit
const COOLDOWN_KEY = 'exit_popup_cooldown'; // avoid immediate reopen
const seen = () => sessionStorage.getItem(SEEN_KEY) === '1';
const setSeen = () => sessionStorage.setItem(SEEN_KEY, '1');
const inCooldown = () => sessionStorage.getItem(COOLDOWN_KEY) === '1';
const startCooldown = (ms=5000) => {
sessionStorage.setItem(COOLDOWN_KEY, '1');
setTimeout(() => sessionStorage.removeItem(COOLDOWN_KEY), ms);
};
// Test thresholds (adjust as you like)
const MIN_SECONDS = 8;
const MIN_SCROLL_Y = 100;
let ready = false;
setTimeout(() => ready = true, MIN_SECONDS * 1000);
function openPopup() {
if (!wrapper || inCooldown()) return;
wrapper.classList.add('is-open');
wrapper.setAttribute('aria-hidden', 'false');
lockScroll();
focusTrap(wrapper);
}
function closePopup() {
if (!wrapper) return;
wrapper.classList.remove('is-open');
wrapper.setAttribute('aria-hidden', 'true');
unlockScroll();
setSeen(); // mark AFTER user dismisses
startCooldown(); // short cooldown
}
// Exit intent (desktop)
function onMouseOut(e) {
const topExit = (e.relatedTarget === null && e.clientY <= 10) || e.clientY <= 0;
if (!ready) return;
if (window.scrollY < MIN_SCROLL_Y) return;
if (topExit && !seen()) openPopup();
}
document.addEventListener('mouseout', onMouseOut);
// Mobile fallback (idle)
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
if (isMobile) {
let idleTimer;
const IDLE_MS = 30000;
const reset = () => {
clearTimeout(idleTimer);
idleTimer = setTimeout(() => { if (!seen()) openPopup(); }, IDLE_MS);
};
['touchstart','scroll','keydown','mousemove'].forEach(evt =>
document.addEventListener(evt, reset, { passive: true })
);
reset();
}
// Close only via explicit close button
closeEls.forEach(btn => btn.addEventListener('click', closePopup));
// Scroll lock
let scrollPos = 0;
function lockScroll() {
scrollPos = window.pageYOffset || document.documentElement.scrollTop;
document.body.style.top = `-${scrollPos}px`;
document.body.style.position = 'fixed';
document.body.style.width = '100%';
}
function unlockScroll() {
document.body.style.position = '';
document.body.style.top = '';
document.body.style.width = '';
window.scrollTo(0, scrollPos);
}
// Focus trap
function focusTrap(modal) {
const focusables = modal.querySelectorAll(
'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
);
const first = focusables[0];
const last = focusables[focusables.length - 1] || first;
(first || modal).focus();
modal.addEventListener('keydown', (e) => {
if (e.key !== 'Tab' || focusables.length === 0) return;
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
});
}
// On-load popup (does NOT set 'seen' anymore)
const SHOW_ON_LOAD = true;
const ONLOAD_DELAY_MS = 300;
if (SHOW_ON_LOAD) {
window.addEventListener('load', () => {
if (!seen() && !inCooldown()) {
setTimeout(openPopup, ONLOAD_DELAY_MS);
}
});
}
// Optional: mark as seen after successful Webflow form submit
document.addEventListener('submit', function(e){
const form = e.target.closest('[data-exit-popup] form');
if (!form) return;
// If you listen for Webflow's success event, mark seen and close after a delay
form.addEventListener('wfp-success', () => {
setSeen();
setTimeout(closePopup, 1200);
}, { once: true });
});
})();
</script>
Let’s get connect on LinkedIn: www.linkedin.com/in/thenurulamin/