Skip to content

Open a fullscreen quiz after a delay or on exit intent

Goal: a product page carries a fullscreen quiz that stays out of the way, then opens after N seconds on the page or when the pointer heads for the tab bar, once per visit.

Uses: Storefront JS API, ready(), close(), open(), quiz.closed. Nothing to install; no API key. The methods work on every plan.

The container starts invisible, the API closes it, a trigger opens it

Section titled “The container starts invisible, the API closes it, a trigger opens it”

A fullscreen quiz shows its overlay as soon as it mounts, and close() can only run once the quiz is mounted (ready). To avoid a flash between the two, hide the container with CSS first; close() then parks the overlay and restores the page’s scrolling, and the CSS comes off so a later open() shows it.

<!-- the container from the dashboard's snippet (Publish > Embed), with data-embed-type="fullscreen",
an id and the style; keep the app.js script tag of that snippet after it -->
<div class="octane-ai-app" id="quiz-popup" data-app-id="7c9e6679-7425-40de-944b-e07fc1f90ae7"
data-bot-id="5f3a2b1c9d8e7f6a5b4c3d2e" data-embed-type="fullscreen" style="visibility: hidden"></div>
<script>
window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
var DELAY_MS = 20000;
var SEEN_KEY = 'octane:popup-shown';
var container = document.getElementById('quiz-popup');
var opened = false;
function openOnce() {
if (opened) return;
opened = true;
try { sessionStorage.setItem(SEEN_KEY, '1'); } catch (e) {}
octaneai.open();
}
octaneai.ready(function (api) {
api.close();
container.style.visibility = '';
var seen = false;
try { seen = sessionStorage.getItem(SEEN_KEY) === '1'; } catch (e) {}
if (seen) return;
setTimeout(openOnce, DELAY_MS);
document.addEventListener('mouseout', function (e) {
if (!e.relatedTarget && e.clientY <= 0) openOnce();
});
});
});
</script>

The mouseout check is the exit-intent signal on desktop: the pointer left the document through its top edge. Phones have no pointer; there the delay is the only trigger.

  • open() and close() before any quiz mounted log [OctaneAI] no quiz on this page yet and do nothing; ready() is what guarantees the quiz is there.
  • sessionStorage throws in some private modes; the try blocks keep the popup working without the once-per-visit memory.
  • close() fires quiz.closed at once, from inside the call, and is counted as a close in the quiz’s analytics, so the parking close at ready counts once per page load. Do not read quiz.closed as “the shopper closed it” on this page: the opened flag already keeps the popup to one open per visit. quiz.viewed fires at mount, before the shopper sees anything.
  • The overlay’s own X sends the shopper to the store’s front page; close() from your code hides the overlay in place and keeps the session, so open() resumes where the shopper was.
  • The session is created at mount, whether or not the popup ever opens; a page load is a session.
  • Keep visibility: hidden (not display: none) on the container: a mounted fullscreen quiz locks the page’s scrolling until close() runs, and display: none would not stop that.
  • Two fullscreen quizzes on one page each get their own overlay; pass the quiz id to open(quizId) to choose.