Skip to content

Loading state

When a page opens, playhtml has to fetch the room’s shared data before elements can show it. Until then, an element would show its default data (a counter at 0, a toggle off). playhtml marks elements as loading during that wait so people don’t act on a default that’s about to change.

Loading lasts until the room syncs, usually well under a second. It starts again whenever navigation moves to a different room.

An element with loading treatment gets these until its room’s data is in:

  • The playhtml-loading class.
  • aria-busy="true", so screen readers know the content is about to change.
  • pointer-events: none, so nobody clicks or drags an element that’s about to change.

How the element looks depends on its kind:

  • Built-in elements (can-move, can-toggle, can-spin, and the rest) fade in and out gently.
  • Custom elements (can-play, withSharedState) get no loading treatment by default, because playhtml can’t know what a sensible placeholder is for your element. They are clickable right away. Setting any of the options below turns loading treatment on.

Choosing how an element looks while loading

Section titled “Choosing how an element looks while loading”

Three attributes control it. They work on any playhtml element.

AttributeValuesWhat it does
loading-behavioranimate, hidden, noneanimate fades the element in and out. hidden keeps it invisible until the data is in. none turns all loading treatment off: no class, no aria-busy, and the element looks normal.
loading-stylebreathing, pulse, fade, noneWhich look to use. breathing is the gentle fade the built-in elements use. pulse is a faster blink. fade holds the element at low opacity. none shows the element as normal, but it keeps the playhtml-loading class and aria-busy, so you can style it yourself.
loading-classany class nameAdds your own class while loading, so you can style the placeholder yourself.

Try each option below. The button uses playhtml’s real loading styles. Switch between loading and loaded, and try clicking it while it loads.

Each choice trades something:

  • animate shows the default value while loading, but makes it clear the value isn’t final.
  • hidden never shows the default. The element still takes up its space, so the page has a blank gap until the data is in.
  • none shows the element as normal right away. People see the default value, and clicking it does nothing until the data is in.
<!-- Invisible until its data is in, so nobody sees the default -->
<div id="scoreboard" can-play loading-behavior="hidden"></div>

<!-- A toggle that pulses instead of breathing -->
<button id="lamp" can-toggle loading-style="pulse">lamp</button>

<!-- Your own placeholder style -->
<div id="guestbook" can-play loading-class="is-placeholder"></div>

<style>
  .is-placeholder {
    background: repeating-linear-gradient(45deg, #eee 0 8px, #f6f6f6 8px 16px);
  }
</style>

You can also style loading elements with plain CSS. Target .playhtml-loading or [aria-busy="true"]:

.playhtml-loading {
  filter: grayscale(1);
}

To render something different while the page loads, such as a “connecting…” line or a skeleton, read playhtml’s loading state directly.

playhtml.isLoading is true until the room’s data is in. A playhtml:loading-change event fires on document each time it changes:

<p id="status">connecting…</p>

<script type="module">
  import { playhtml } from "playhtml";

  const status = document.getElementById("status");
  const render = () => {
    status.hidden = !playhtml.isLoading;
  };

  document.addEventListener("playhtml:loading-change", render);
  playhtml.init();
  render();
</script>

To run code once the data is in, wait for playhtml.ready:

await playhtml.ready;
console.log("data is in");

Loading isn’t only at page open. When client-side navigation moves to a different room, the new room’s data has to load too:

  • isLoading turns true again, playhtml:loading-change fires, and elements get their loading treatment.
  • playhtml.ready becomes a new promise for the new room. Read playhtml.ready again instead of reusing one you saved earlier.

Navigating within the same room doesn’t trigger loading. See Navigation & SPAs for how rooms are picked.