Signatur
Beschreibung
CustomElementRegistry ist die zentrale Registrierungsstelle für Custom Elements, einen Teil der Web-Components-Technik. Du erzeugst keine eigene Instanz. Die globale Registry erreichst du über window.customElements. Dort verknüpfst du einen Tag-Namen wie <user-card> mit einer JavaScript-Klasse, die von HTMLElement oder einem eingebauten Element erbt.
Die wichtigsten Methoden:
define(name, constructor, options): registriert ein neues Element. Bereits vorhandene Elemente im DOM werden sofort „aufgewertet“ (Upgrade).get(name): gibt den Konstruktor eines registrierten Elements zurück, sonstundefined.getName(constructor): gibt den registrierten Namen zu einer Klasse zurück, sonstnull(ab 2024 verfügbar).whenDefined(name): liefert einPromise, das erfüllt wird, sobald das Element definiert ist. Das ist nützlich bei Lazy Loading und um das Aufblitzen noch nicht gestylter Inhalte (FOUC) zu vermeiden.upgrade(root): wertet Custom Elements in einem Teilbaum auf, der noch nicht mit dem Dokument verbunden ist.
Du brauchst die Registry immer dann, wenn du wiederverwendbare, framework-unabhängige Komponenten baust, Komponenten erst bei Bedarf nachlädst oder vor einem Zugriff prüfen musst, ob eine Komponente schon verfügbar ist.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Parameter von define(), get() und whenDefined(): der Tag-Name. Er muss einen Bindestrich enthalten, mit einem Kleinbuchstaben beginnen und darf keine Großbuchstaben enthalten, z. B. my-button. |
|
| $constructor Pflicht | CustomElementConstructor | Parameter von define() und getName(): eine Klasse, die von HTMLElement erbt (bei eingebauten Elementen z. B. von HTMLButtonElement). |
|
| $options | { extends?: string } | undefined | Optionaler Parameter von define(). extends gibt das eingebaute Element an, das erweitert wird (Customized Built-in Element), z. B. { extends: 'button' }. |
| $root Pflicht | Node | Parameter von upgrade(): der Wurzelknoten, dessen Nachfahren aufgewertet werden sollen. |
Rückgabewert
window.customElements erreichbar. Rückgabewerte der Methoden: define() und upgrade() liefern undefined, get() liefert CustomElementConstructor | undefined, getName() liefert string | null und whenDefined() liefert Promise<CustomElementConstructor>.Beispiele
Eigenes Element definieren und verwenden
class UserCard extends HTMLElement {
static observedAttributes = ['name'];
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
this.textContent = `Hallo, ${this.getAttribute('name') ?? 'Gast'}!`;
}
}
// Doppelte Registrierung vermeiden
if (!customElements.get('user-card')) {
customElements.define('user-card', UserCard);
}
const card = document.createElement('user-card');
card.setAttribute('name', 'Anna');
document.body.append(card);
console.log(card.textContent);
console.log(customElements.getName(UserCard));
Mit whenDefined() auf eine nachgeladene Komponente warten
// Komponente erst bei Bedarf laden
const loadDatePicker = () => import('./date-picker.js'); // ruft darin customElements.define('date-picker', …) auf
loadDatePicker();
const DatePicker = await customElements.whenDefined('date-picker');
console.log('Bereit:', DatePicker.name);
// Alle noch nicht definierten Elemente abwarten und erst dann einblenden
const undefinedTags = [...document.querySelectorAll(':not(:defined)')]
.map((el) => el.localName);
await Promise.all([...new Set(undefinedTags)].map((tag) => customElements.whenDefined(tag)));
document.body.classList.add('ready');
Eingebautes Element erweitern und upgrade() nutzen
class ConfirmButton extends HTMLButtonElement {
connectedCallback() {
this.addEventListener('click', (e) => {
if (!confirm('Wirklich ausführen?')) e.preventDefault();
});
}
}
customElements.define('confirm-button', ConfirmButton, { extends: 'button' });
// Teilbaum außerhalb des Dokuments erzeugen
const template = document.createElement('template');
template.innerHTML = '<button is="confirm-button">Löschen</button>';
const fragment = template.content.cloneNode(true);
const btn = fragment.querySelector('button');
console.log(btn instanceof ConfirmButton); // noch nicht aufgewertet
customElements.upgrade(fragment);
console.log(btn instanceof ConfirmButton);
// Wichtig · Fallstricke
- Doppelte Registrierung: Wird
define()zweimal mit demselben Namen oder derselben Klasse aufgerufen, wirft es eineNotSupportedError-DOMException. Prüfe das vorher mitcustomElements.get(), besonders bei Hot Module Replacement oder mehrfach eingebundenen Skripten. - Ungültige Namen ohne Bindestrich oder mit Großbuchstaben (z. B.
myelement,My-El) führen zu einemSyntaxError. Reservierte Namen wiefont-facesind ebenfalls verboten. - Konstruktor-Regeln: Im
constructorzuerstsuper()aufrufen. Dort keine Attribute oder Kindelemente anlegen oder lesen. DOM-Arbeit gehört inconnectedCallback. - Customized Built-ins (
{ extends: 'button' }mit dem Attributis=\"…\") unterstützt Safari/WebKit nicht. Für browserübergreifenden Code besser autonome Elemente verwenden oder ein Polyfill einsetzen. getName()ist neu (Baseline 2024). In älteren Browsern vorher mit'getName' in customElementsprüfen.- Namen gelten global pro Registry. Zwei Bibliotheken mit demselben Tag-Namen kollidieren. Nutze deshalb Präfixe (z. B.
acme-button). Eigene, begrenzte Registries (Scoped Custom Element Registries) befinden sich noch in der Standardisierung. - Die Pseudoklasse
:not(:defined)in CSS verhindert das Aufblitzen noch nicht definierter Elemente.