javascriptcssweb-componentshadow-domnative-web-component

Hide Light DOM (Nested Elements) on Web Components While Waiting for Slot Absorption to Prevent Flashing Temporary Rendering of Content


I want to prevent showing light DOM content while it's waiting to be absorbed into a <slot>. I've tried many workarounds.

Here's my use case:

<custom-select>
  <custom-option>One</custom-option>
  <custom-option>Two</custom-option>
  <custom-option>Three</custom-option>
</custom-select>

Both types of elements have a default <slot></slot> for the light DOM (nested content).

When the page loads, I see the raw text "One", "Two", and "Three" for however long it takes for the page to load (it's very obvious on heavy page loads), and then everything is absorbed into their <slot>s and is hidden as expected.

I've tried :host and :host children hiding, hiding by default and then adding a class to show it, and other dynamic things like that. Not even doing this.style.display = 'none'; before attaching the shadowRoot in the constructor works! Crazy stuff. All styling happens after it's rendered to the DOM regardless of how styling is implemented.

Is there not a simple solution that will hide it immediately (and ideally always hide the light DOM and allow the showing of the light DOM in the slot)?

I do NOT want an external stylesheet controlling this, though that accomplishes the ultimate need (i.e., hiding an custom element, and then the custom element adds a class later that allows the CSS to show it). The point is to let components do their thing, not control a nuance--externally defeating the whole purpose of componentized elements and their micro DOMs.


Solution

  • Alas, there is no way to completely prevent FOUC from loading a style via the Web Component. The issue initially occurs in the document, where the light DOM sits. If you want to avoid document content from showing before the shadow DOM of the Custom Element is loaded, then you will need to add a style (sheet) to tell the document what to do. The Web Component doesn't 'own' slotted content.

    custom-select:has(:not(:defined)){
        visibility: hidden;
    }