Signatur
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
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());
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(', ')}`);
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);
}
// 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
undefinedstatt mit dem Konstruktor erfüllt. Wer den Konstruktor braucht, kann ersatzweisecustomElements.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 mitcustomElements.upgrade()nachholen. - Wer nur ein Aufblitzen ungestylter Elemente vermeiden will, braucht oft kein JavaScript:
my-el:not(:defined) { visibility: hidden; }reicht dafür aus.