Start · Sprachen · JavaScript · Referenz · CustomElementRegistry.whenDefined()

CustomElementRegistry.whenDefined()

Methode

Gibt ein Promise zurück, das erfüllt wird, sobald ein Custom Element mit dem angegebenen Namen per <code>customElements.define()</code> registriert ist.

seit JavaScript Baseline: alle modernen Browser (seit 2020) Kategorie: webapi

Signatur

CustomElementRegistry.prototype.whenDefined(name)

Beschreibung

whenDefined() wird meist über das globale Objekt window.customElements aufgerufen. Die Methode liefert ein Promise, das erfüllt wird, sobald ein Element mit dem übergebenen Namen registriert ist. Ist das Element bereits definiert, wird das Promise sofort erfüllt. Die Reaktion darauf läuft trotzdem asynchron im nächsten Microtask.

Das ist vor allem nützlich, wenn Web Components verzögert oder asynchron geladen werden, etwa per dynamischem import(), Lazy Loading oder über Skripte von Drittanbietern. Code, der die Methoden oder Eigenschaften eines Custom Elements braucht, kann so warten, bis das Element hochgestuft wurde (engl. „upgrade“). Erst danach besitzt das Element seine Klasse samt eigener API.

Ein typischer Einsatz ist das Verhindern von FOUC (Flash of Unstyled Content): Solange die Komponenten noch nicht definiert sind, zeigt die Seite einen Platzhalter. Erst wenn alle Komponenten bereit sind, wird der eigentliche Inhalt sichtbar. Mit CSS lässt sich das ergänzend über die Pseudoklasse :defined bzw. :not(:defined) lösen.

Parameter

Name Typ Default Beschreibung
$name Pflicht string Name des Custom Elements, z. B. "my-button". Das muss ein gültiger Custom-Element-Name sein: kleingeschrieben, mit mindestens einem Bindestrich und kein reservierter Name wie font-face.

Rückgabewert

Typ
Promise<CustomElementConstructor>
Beschreibung
Ein Promise, das mit dem Konstruktor (der Klasse) des definierten Elements erfüllt wird. Ist der Name ungültig, wird das Promise mit einer DOMException vom Typ SyntaxError zurückgewiesen.

Beispiele

Auf eine Komponente warten und ihre API nutzen

// Die Komponente wird erst später definiert, z. B. per Lazy Loading
setTimeout(() => {
  customElements.define('user-card', class extends HTMLElement {
    greet() { return `Hallo, ${this.getAttribute('name')}!`; }
  });
}, 500);

const card = document.createElement('user-card');
card.setAttribute('name', 'Anna');
document.body.append(card);

const UserCard = await customElements.whenDefined('user-card');
console.log(card instanceof UserCard);
console.log(card.greet());
true Hallo, Anna!

Platzhalter ausblenden, sobald alle Komponenten bereit sind

// HTML: <div id="app" hidden>…</div> <p id="loader">Lädt…</p>
const app = document.querySelector('#app');

// Alle noch nicht definierten Custom Elements im Container ermitteln
const undefinedTags = [...app.querySelectorAll(':not(:defined)')]
  .map((el) => el.localName);

const uniqueTags = [...new Set(undefinedTags)];

await Promise.all(uniqueTags.map((tag) => customElements.whenDefined(tag)));

document.querySelector('#loader').remove();
app.hidden = false;
console.log(`Bereit: ${uniqueTags.join(', ')}`);
Bereit: app-header, app-sidebar

Mit Zeitlimit warten und ungültige Namen abfangen

const waitForElement = (name, ms = 3000) =>
  Promise.race([
    customElements.whenDefined(name),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error(`${name} nicht definiert nach ${ms} ms`)), ms)
    ),
  ]);

try {
  await waitForElement('ohneBindestrich');
} catch (err) {
  console.log(err.name); // ungültiger Name
}

try {
  await waitForElement('never-defined', 1000);
} catch (err) {
  console.log(err.message);
}
SyntaxError never-defined nicht definiert nach 1000 ms

// Wichtig · Fallstricke

  • Kein eingebautes Zeitlimit: Wird das Element nie definiert, bleibt das Promise für immer offen. Für kritische Abläufe kombiniert man es daher mit einem Zeitlimit über Promise.race().
  • Ungültige Namen (z. B. ohne Bindestrich oder mit Großbuchstaben) führen zu einem zurückgewiesenen Promise mit SyntaxError. Die Methode wirft dabei keine synchrone Ausnahme.
  • Rückgabewert: In älteren Browsern (vor 2020, z. B. Chrome < 84) wurde das Promise mit undefined statt mit dem Konstruktor erfüllt. Wer den Konstruktor braucht, kann ersatzweise customElements.get(name) verwenden.
  • Das Promise wird erfüllt, sobald die Klasse registriert ist. Einzelne Elemente außerhalb des Dokuments (z. B. in einem <template>-Inhalt) sind dann aber möglicherweise noch nicht hochgestuft. Das lässt sich mit customElements.upgrade() nachholen.
  • Wer nur ein Aufblitzen ungestylter Elemente vermeiden will, braucht oft kein JavaScript: my-el:not(:defined) { visibility: hidden; } reicht dafür aus.