How to Show Loading Screen Only on First Visit and Hide on Refresh

Hi,

I want my site to show a loading overlay only when someone first visits the homepage.

On their first visit:

  • Show the loading overlay
  • Auto-scroll to the hero section
  • Lock scrolling during loading so no one can manually scroll past it

On any subsequent refresh or revisit:

  • The loading overlay should not show
  • The page should load directly at the hero section, with no scroll lock

What I need help with:

  • How do I set up my code so that the loading overlay appears only on the first visit?
  • How do I make sure refreshing the page doesn’t show the overlay?
  • How do I prevent the scroll lock from remaining active after the first visit?

I’m using:

  • sessionStorage to track if it’s the first visit
  • Custom code for showing/hiding the overlay and locking scroll
My velo code on home right now

import wixWindowFrontend from ‘wix-window-frontend’;

import wixLocationFrontend from ‘wix-location-frontend’;

import { session } from ‘wix-storage-frontend’;

$w.onReady(async () => {

console.log(‘onReady fired’);

const alreadyPlayed = session.getItem(‘introPlayed’);

console.log(‘alreadyPlayed value:’, alreadyPlayed);

const currentUrl = wixLocationFrontend.url;

const urlParts = currentUrl.split(‘#’);

const anchorId = urlParts.length > 1 ? urlParts[1] : null;

if (alreadyPlayed === ‘true’) {

console.log(‘Taking SKIP branch’);

try {

await $w(‘#loading-page’).collapse();

console.log(‘collapse() succeeded’);

} catch (e) {

console.log(‘collapse() FAILED:’, e.message);

}

if (anchorId) {

setTimeout(() => { $w(‘#’ + anchorId).scrollTo(); }, 50);

}

} else {

console.log(‘Taking PLAY INTRO branch’);

session.setItem(‘introPlayed’, ‘true’);

try {

await $w(‘#loading-page’).expand();

console.log(‘expand() succeeded’);

} catch (e) {

console.log(‘expand() FAILED:’, e.message);

}

setTimeout(async () => {

console.log(‘3100ms timer fired’);

if (wixLocationFrontend.url.includes(‘#’)) {

await $w(‘#loading-page’).collapse();

return;

}

await $w(‘#section1’).scrollTo();

await $w(‘#loading-page’).collapse();

wixWindowFrontend.scrollTo(0, 0, { scrollAnimation: false });

console.log(‘Intro sequence complete’);

}, 3100);

}

$w(‘#colour-trigger-line’).onViewportLeave(() => {

$w(‘#feral-logo-white, #menu-white’).hide(“fade”);

$w(‘#feral-logo-red, #menu-red’).show(“fade”);

});

$w(‘#colour-trigger-line’).onViewportEnter(() => {

$w(‘#feral-logo-white, #menu-white’).show(“fade”);

$w(‘#feral-logo-red, #menu-red’).hide(“fade”);

});

});

My lock scroll custom header code in settings for home right now

Thanks in advance!

Your second details block came through empty, so I can’t see the lock code itself. The rest answers the three questions.

Refresh and revisit are two different lifetimes, and session only covers one of them. Session storage survives a reload, but it ends when the visitor closes the tab or window, so a return visit plays the intro again. local is the one that matches what you described, since it “never expires, even if a site visitor closes a page”. Swapping session for local answers your first two questions. Storage API

The lock stays on because nothing releases it. There is no unlock call in the page code you posted, on either branch. The lock lives in the head snippet, which loads with the page head on whichever Add Code to Pages option you chose, not on your introPlayed branch. So the release belongs next to the lock, in the same snippet, on a timer that always fires.

This goes in the home page’s code:

import wixWindowFrontend from 'wix-window-frontend';
import { local } from 'wix-storage-frontend';

const INTRO_MS = 3100;   // must match the head snippet
const INTRO_KEY = 'introPlayed';

$w.onReady(() => {
  // onReady runs on the server and again in the browser on the first load, so
  // anything with a side effect runs twice unless you keep it to the browser pass.
  if (wixWindowFrontend.rendering.env !== 'browser') {
    return;
  }

  if (local.getItem(INTRO_KEY) === 'true') {
    return settleAtHero();
  }

  local.setItem(INTRO_KEY, 'true');
  return playIntro();
});

async function playIntro() {
  await $w('#loading-page').expand();
  await new Promise((resolve) => setTimeout(resolve, INTRO_MS));
  await settleAtHero();
}

async function settleAtHero() {
  // Collapse before scrolling. A collapsed element takes up no space, so
  // everything below it moves up, and a scroll done first lands in the wrong
  // place. That is what your trailing scrollTo(0, 0) was compensating for.
  await $w('#loading-page').collapse();
  wixWindowFrontend.scrollTo(0, 0, { scrollAnimation: false });
}

This goes in Settings > Custom Code > Head, added to the home page only:

<script>
(function () {
  var KEY = 'introLockDone';
  var INTRO_MS = 3100;
  var seen;

  try { seen = window.localStorage.getItem(KEY); } catch (error) { return; }
  if (seen === 'true') { return; }
  try { window.localStorage.setItem(KEY, 'true'); } catch (error) {}

  function setOverflow(value) {
    // If your snippet locks a different way, keep your own two lines here.
    document.documentElement.style.overflow = value;
    if (document.body) { document.body.style.overflow = value; }
  }

  setOverflow('hidden');
  document.addEventListener('DOMContentLoaded', function () { setOverflow('hidden'); });

  setTimeout(function () { setOverflow(''); }, INTRO_MS);
  window.addEventListener('pageshow', function (event) {
    if (event.persisted) { setOverflow(''); }
  });
})();
</script>

Set #loading-page collapsed in the editor so returning visitors never see it flash. There are now two flags, one per file, so clear both between test runs. Your colour-trigger handlers are unaffected and I left them out above.

When you test, note that custom code only runs once the site is published with a connected domain, and that rendering never happens server side in Preview, so the rendering.env guard behaves differently there than on the live site.

I haven’t run this against a live site.