Add to: All pages
Place in: Body - end
Then, in the Editor, give your existing Quick Exit button the ID
"quickExit" (Wix: select element -> Properties panel -> ID), or simply
leave it as a link — the script intercepts any link whose text contains
"quick exit".
INSTALL ANYWHERE ELSE
Vanilla JS, no dependencies, ~2KB. MIT-style: use and modify freely.
========================================================================== */
(function () {
'use strict';
/* --- Configuration ------------------------------------------------------ */
// Where the visitor is sent. Weather is the strongest choice: it is the most
// plausible thing to have been looking at, it is not a search engine (a blank
// Google page invites "what were you searching for?"), and it loads fast.
var EXIT_URL = 'https://www.bbc.co.uk/weather';
// Opened in a second tab so the browser lands on something with real content.
var DECOY_URL = 'https://www.bbc.co.uk/news';
// Set this once the page exists. Leave null to skip the link.
var COVER_YOUR_TRACKS_URL = null; // e.g. '/support/cover-your-tracks'
// The title left in the session before leaving.
var NEUTRAL_TITLE = 'Weather';
var DOUBLE_TAP_WINDOW = 1000; // ms, for the Escape shortcut
var SHIFT_TAPS = 3;
/* --- The exit ----------------------------------------------------------- */
var exiting = false;
function quickExit() {
if (exiting) return;
exiting = true;
// 1. Blank the screen immediately. This is the part that matters on a slow
// connection: navigation may take a second, but the content is gone now.
try {
var veil = document.createElement('div');
veil.setAttribute('role', 'presentation');
veil.style.cssText =
'position:fixed;inset:0;background:#fff;z-index:2147483647;';
document.documentElement.appendChild(veil);
} catch (e) {}
// 2. Clear anything typed into a form.
try {
var fields = document.querySelectorAll('input, textarea, select');
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
if (f.type === 'checkbox' || f.type === 'radio') f.checked = false;
else f.value = '';
}
if (window.sessionStorage) sessionStorage.clear();
} catch (e) {}
// 3. Make the entry we are about to overwrite unidentifiable.
try {
document.title = NEUTRAL_TITLE;
history.replaceState(null, NEUTRAL_TITLE, '/');
} catch (e) {}
// 4. Open the decoy tab. Must happen inside the user gesture or popup
// blockers will stop it. If blocked, the replace below still runs.
try {
var decoy = window.open(DECOY_URL, '_blank');
if (decoy) decoy.focus();
} catch (e) {}
// 5. Replace — not assign. assign() pushes a new history entry and leaves
// this page reachable via Back. replace() overwrites it.
try {
window.location.replace(EXIT_URL);
} catch (e) {
window.location.href = EXIT_URL;
}
}
/* --- Wiring ------------------------------------------------------------- */
function isExitTrigger(el) {
while (el && el !== document.body) {
if (el.nodeType === 1) {
if (
el.hasAttribute('data-quick-exit') ||
el.id === 'quickExit' ||
(el.className &&
typeof el.className === 'string' &&
el.className.indexOf('quick-exit') !== -1)
) {
return true;
}
// Catches the existing Wix link by its text, so it works before you
// have edited anything in the Editor.
if (
el.tagName === 'A' &&
(el.textContent || '').toLowerCase().indexOf('quick exit') !== -1
) {
return true;
}
}
el = el.parentNode;
}
return false;
}
document.addEventListener(
'click',
function (e) {
if (isExitTrigger(e.target)) {
e.preventDefault();
e.stopPropagation();
quickExit();
}
},
true // capture phase, so Wix's own handlers cannot swallow it first
);
var lastEscape = 0;
var shiftTaps = 0;
var shiftTimer = null;
document.addEventListener(
'keydown',
function (e) {
// Escape twice within the window. Two presses rather than one, so an
// accidental Escape while closing a menu does not throw the visitor out.
if (e.key === 'Escape' || e.keyCode === 27) {
var now = Date.now();
if (now - lastEscape < DOUBLE_TAP_WINDOW) {
quickExit();
return;
}
lastEscape = now;
}
// Shift, three times. A fallback for when something else has taken
// Escape, and quieter to press than Escape on a shared keyboard.
if (e.key === 'Shift' || e.keyCode === 16) {
shiftTaps++;
clearTimeout(shiftTimer);
if (shiftTaps >= SHIFT_TAPS) {
shiftTaps = 0;
quickExit();
return;
}
shiftTimer = setTimeout(function () {
shiftTaps = 0;
}, DOUBLE_TAP_WINDOW);
}
},
true
);
/* --- Make sure an exit control always exists ----------------------------- */
/* If no Quick Exit element is found on a page, this injects one. Belt and
braces: a page that is missing the button is the page someone lands on. */
function ensureButton() {
if (
document.querySelector(
'[data-quick-exit], #quickExit, .quick-exit'
)
) {
return;
}
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
if ((links[i].textContent || '').toLowerCase().indexOf('quick exit') !== -1) {
return;
}
}
var btn = document.createElement('button');
btn.type = 'button';
btn.setAttribute('data-quick-exit', '');
btn.setAttribute(
'aria-label',
'Quick exit. Leaves this site immediately. Also press Escape twice.'
);
btn.textContent = 'Quick exit';
btn.style.cssText = [
'position:fixed', 'top:0', 'right:0', 'z-index:2147483646',
'margin:.5rem', 'padding:.6rem 1rem',
'font:600 14px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif',
'color:#fff', 'background:#B3242B', 'border:0', 'border-radius:4px',
'cursor:pointer', 'box-shadow:0 2px 8px rgba(0,0,0,.25)'
].join(';');
document.body.appendChild(btn);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', ensureButton);
} else {
ensureButton();
}
// Exposed so you can trigger it from anywhere, e.g. a Wix Velo onClick:
// $w('#myButton').onClick(() => window.quickExit());
window.quickExit = quickExit;
window.QUICK_EXIT_COVER_TRACKS_URL = COVER_YOUR_TRACKS_URL;
})();