top of page
bottom of page
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; })();