Start · Sprachen · JavaScript · Referenz · Window.customElements

Window.customElements

Eigenschaft

Gibt das globale <code>CustomElementRegistry</code>-Objekt zurück, mit dem du benutzerdefinierte HTML-Elemente (Web Components) registrieren und abfragen kannst.

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

Signatur

get Window.customElements

Beschreibung

Die schreibgeschützte Eigenschaft window.customElements liefert eine Referenz auf das CustomElementRegistry des aktuellen Dokuments. Über dieses Objekt meldest du eigene Elemente (Custom Elements) beim Browser an. Danach kannst du sie wie normale HTML-Tags verwenden, etwa <user-card>.

Die wichtigsten Methoden des Registry-Objekts sind:

  • define(name, constructor, options) registriert ein neues Element.
  • get(name) gibt den Konstruktor eines registrierten Elements zurück oder undefined.
  • getName(constructor) liefert umgekehrt den registrierten Namen zu einem Konstruktor.
  • whenDefined(name) gibt ein Promise zurück, das erfüllt wird, sobald das Element definiert ist.
  • upgrade(root) wertet Elemente in einem Teilbaum auf, die noch nicht verbunden sind.

Du brauchst customElements immer dann, wenn du wiederverwendbare, gekapselte UI-Komponenten ohne Framework baust. Oft geschieht das zusammen mit Shadow DOM und <template>. Da customElements eine globale Eigenschaft ist, kannst du im Browser auch einfach customElements statt window.customElements schreiben.

Rückgabewert

Typ
CustomElementRegistry
Beschreibung
Das globale Registry-Objekt für benutzerdefinierte Elemente des aktuellen Fensters bzw. Dokuments.

Beispiele

Autonomes Custom Element registrieren

class UserCard extends HTMLElement {
  connectedCallback() {
    const name = this.getAttribute('name') ?? 'Unbekannt';
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `<style>p { font-weight: bold; }</style><p></p>`;
    shadow.querySelector('p').textContent = `Hallo, ${name}!`;
  }
}

customElements.define('user-card', UserCard);

// Verwendung 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.shadowRoot.textContent.trim());
p { font-weight: bold; }Hallo, Anna!

Doppelte Registrierung vermeiden mit get()

const defineOnce = (name, ctor) => {
  if (!customElements.get(name)) {
    customElements.define(name, ctor);
    console.log(`${name} registriert`);
  } else {
    console.log(`${name} existiert bereits`);
  }
};

class AppBadge extends HTMLElement {}

defineOnce('app-badge', AppBadge);
defineOnce('app-badge', AppBadge);
app-badge registriert app-badge existiert bereits

Auf Definition warten mit whenDefined()

const initDialog = async () => {
  await customElements.whenDefined('my-dialog');
  const dialog = document.querySelector('my-dialog');
  console.log('Bereit:', dialog instanceof customElements.get('my-dialog'));
};

initDialog();

// Später, z. B. nach dynamischem Nachladen eines Moduls:
setTimeout(() => {
  document.body.insertAdjacentHTML('beforeend', '<my-dialog></my-dialog>');
  customElements.define('my-dialog', class extends HTMLElement {});
}, 500);
Bereit: true

Eingebautes Element erweitern (customized built-in)

class ConfirmButton extends HTMLButtonElement {
  constructor() {
    super();
    this.addEventListener('click', (e) => {
      if (!confirm('Wirklich fortfahren?')) e.preventDefault();
    });
  }
}

customElements.define('confirm-button', ConfirmButton, { extends: 'button' });

// HTML: <button is="confirm-button">Löschen</button>
const btn = document.createElement('button', { is: 'confirm-button' });
btn.textContent = 'Löschen';
document.body.append(btn);

// Wichtig · Fallstricke

  • Namensregeln: Der Elementname muss einen Bindestrich enthalten und mit einem Kleinbuchstaben beginnen, z. B. my-element. Sonst wirft define() einen SyntaxError.
  • Keine doppelte Registrierung: Wird derselbe Name oder derselbe Konstruktor ein zweites Mal registriert, wirft define() einen NotSupportedError. Das passiert leicht bei Hot Module Reload oder wenn ein Skript mehrfach geladen wird. Prüfe deshalb vorher mit customElements.get(). Einmal registrierte Elemente lassen sich nicht mehr abmelden.
  • Konstruktor-Einschränkungen: Im constructor musst du zuerst super() aufrufen. Dort solltest du keine Attribute oder Kindelemente lesen oder setzen. Solche Arbeit gehört in connectedCallback.
  • Customized built-ins ({ extends: 'button' }, Attribut is) unterstützt Safari/WebKit nicht. Für diese Fälle brauchst du ein Polyfill oder ein autonomes Element.
  • Geltungsbereich: Jedes Fenster und jedes <iframe> hat ein eigenes Registry. In Web Workern und in Node.js gibt es customElements nicht.
  • Sicherheit: Wenn du Inhalte über innerHTML in den Shadow DOM einfügst, musst du Benutzerdaten maskieren oder textContent verwenden. Sonst droht XSS.