Signatur
Beschreibung
Mit define() registrieren Sie ein Custom Element in der CustomElementRegistry, in der Regel über das globale Objekt window.customElements. Danach erkennt der Browser den Tag-Namen im HTML und in document.createElement() und erzeugt Instanzen der angegebenen Klasse. Elemente, die schon vorher im Dokument standen, werden bei der Registrierung automatisch aufgewertet (Upgrade): Der Konstruktor läuft nachträglich, danach werden die Lebenszyklus-Rückrufe aufgerufen.
Es gibt zwei Arten von Custom Elements:
- Autonome Elemente erben von
HTMLElementund werden direkt als eigener Tag verwendet, z. B.<user-card>. - Angepasste eingebaute Elemente erben von einem bestimmten Element (z. B.
HTMLButtonElement). Sie brauchenoptions.extendsund werden über dasis-Attribut genutzt:<button is="fancy-button">.
In der Klasse können Sie Lebenszyklus-Rückrufe wie connectedCallback, disconnectedCallback, adoptedCallback und attributeChangedCallback definieren. Für den letzten braucht es das statische Getter observedAttributes. Der Browser liest diese Rückrufe einmal bei der Registrierung aus. Wenn Sie den Prototyp später ändern, hat das keine Wirkung mehr.
Der Name muss ein gültiger Custom-Element-Name sein. Er muss mit einem Kleinbuchstaben beginnen, mindestens einen Bindestrich enthalten und darf keine Großbuchstaben enthalten. Außerdem ist er nicht auf der Liste reservierter Namen wie font-face oder annotation-xml. Jeder Name und jede Klasse lässt sich pro Registry nur einmal registrieren.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Tag-Name des neuen Elements, z. B. "user-card". Muss einen Bindestrich enthalten, mit einem Kleinbuchstaben beginnen und darf keine Großbuchstaben enthalten. |
|
| $constructor Pflicht | CustomElementConstructor (class extends HTMLElement) | Die Klasse des Elements. Bei autonomen Elementen erbt sie von HTMLElement, bei angepassten eingebauten Elementen von der passenden Schnittstelle (z. B. HTMLButtonElement). |
|
| $options | { extends?: string } | undefined | Optionales Objekt. extends gibt den Namen des eingebauten Elements an, das erweitert wird (z. B. "button"). Das ist nur für angepasste eingebaute Elemente nötig. |
Rückgabewert
customElements.whenDefined(name).Beispiele
Autonomes Element mit Shadow DOM registrieren
class UserCard extends HTMLElement {
constructor() {
super(); // Pflicht: zuerst super() aufrufen
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const name = this.getAttribute('name') ?? 'Unbekannt';
this.shadowRoot.innerHTML = `
<style>:host { display: block; padding: .5rem; border: 1px solid #ccc; }</style>
<strong></strong>
`;
// textContent statt innerHTML für Benutzerdaten (XSS-Schutz)
this.shadowRoot.querySelector('strong').textContent = name;
}
}
customElements.define('user-card', UserCard);
// Verwendung per JS (oder im HTML: <user-card name="Anna"></user-card>)
const card = document.createElement('user-card');
card.setAttribute('name', 'Anna');
document.body.append(card);
console.log(card instanceof UserCard);
Auf Attributänderungen reagieren
class CounterBadge extends HTMLElement {
static get observedAttributes() {
return ['count'];
}
attributeChangedCallback(attr, oldValue, newValue) {
console.log(`${attr}: ${oldValue} → ${newValue}`);
this.textContent = `🔔 ${newValue}`;
}
}
customElements.define('counter-badge', CounterBadge);
const badge = document.createElement('counter-badge');
document.body.append(badge);
badge.setAttribute('count', '3');
badge.setAttribute('count', '4');
Angepasstes eingebautes Element mit extends
class ConfirmButton extends HTMLButtonElement {
connectedCallback() {
this.addEventListener('click', (event) => {
if (!confirm(this.dataset.question ?? 'Sind Sie sicher?')) {
event.preventDefault();
}
});
}
}
customElements.define('confirm-button', ConfirmButton, { extends: 'button' });
// HTML: <button is="confirm-button" data-question="Wirklich löschen?">Löschen</button>
const btn = document.createElement('button', { is: 'confirm-button' });
btn.textContent = 'Löschen';
document.body.append(btn);
Doppelte Registrierung sicher vermeiden
const defineOnce = (name, ctor, options) => {
if (!customElements.get(name)) {
customElements.define(name, ctor, options);
}
};
class AppLogo extends HTMLElement {}
defineOnce('app-logo', AppLogo);
defineOnce('app-logo', AppLogo); // kein Fehler
await customElements.whenDefined('app-logo');
console.log('app-logo ist bereit');
// Wichtig · Fallstricke
- Ausnahmen: Bei einem ungültigen Namen wirft
define()einenSyntaxError(DOMException). EinNotSupportedErrorentsteht, wenn der Name oder die Klasse schon registriert ist oderextendsein Custom Element bzw. einen unbekannten Tag angibt. Ist der Konstruktor keine Konstruktorfunktion, gibt es einenTypeError. Bei Hot Reload oder wenn Skripte mehrfach geladen werden, prüfen Sie vorher mitcustomElements.get(). - Einmal registriert, immer registriert: Eine Definition lässt sich nicht aufheben oder ersetzen.
- Einschränkungen im Konstruktor: Rufen Sie zuerst
super()auf. Lesen oder setzen Sie im Konstruktor keine Attribute und Kindelemente. Das gehört inconnectedCallback, sonst schlägtdocument.createElement()fehl. - Safari/WebKit unterstützt angepasste eingebaute Elemente (
extends/is) nicht. Dafür gibt es ein Polyfill (@ungap/custom-elements). Autonome Elemente funktionieren in allen modernen Browsern. - Bis zur Registrierung sind unbekannte Tags vom Typ
HTMLElement. Solche Elemente können Sie mit dem CSS-Selektor:not(:defined)ausblenden, damit sie nicht kurz ungestylt aufblitzen. - Sicherheit: Setzen Sie Benutzerdaten nicht per
innerHTMLin das Shadow DOM ein. Das Shadow DOM schützt nicht vor XSS.