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

CustomElementRegistry.define()

Methode

Registriert ein benutzerdefiniertes Element: Der Tag-Name wird mit einer Klasse verknüpft, die von <code>HTMLElement</code> oder einem eingebauten Element erbt.

seit JavaScript Baseline: alle modernen Browser (Custom Elements v Kategorie: webapi

Signatur

CustomElementRegistry.prototype.define(name, constructor, options)

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 HTMLElement und werden direkt als eigener Tag verwendet, z. B. <user-card>.
  • Angepasste eingebaute Elemente erben von einem bestimmten Element (z. B. HTMLButtonElement). Sie brauchen options.extends und werden über das is-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

Typ
undefined
Beschreibung
Kein Rückgabewert. Wenn Sie auf den Abschluss der Registrierung warten möchten, nutzen Sie 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);
true

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');
count: null → 3 count: 3 → 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');
app-logo ist bereit

// Wichtig · Fallstricke

  • Ausnahmen: Bei einem ungültigen Namen wirft define() einen SyntaxError (DOMException). Ein NotSupportedError entsteht, wenn der Name oder die Klasse schon registriert ist oder extends ein Custom Element bzw. einen unbekannten Tag angibt. Ist der Konstruktor keine Konstruktorfunktion, gibt es einen TypeError. Bei Hot Reload oder wenn Skripte mehrfach geladen werden, prüfen Sie vorher mit customElements.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 in connectedCallback, sonst schlägt document.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 innerHTML in das Shadow DOM ein. Das Shadow DOM schützt nicht vor XSS.