Start · Sprachen · JavaScript · Referenz · ElementInternals.role

ElementInternals.role

Eigenschaft

Liest oder setzt die WAI-ARIA-Standardrolle eines benutzerdefinierten Elements, ohne dafür ein sichtbares <code>role</code>-Attribut in das DOM zu schreiben.

seit JavaScript Baseline 2023: Chrome 90+, Edge 90+, Safari 16.4+, Kategorie: webapi

Signatur

ElementInternals.role

Beschreibung

Die Eigenschaft role der Schnittstelle ElementInternals gehört zum Mechanismus ARIA-Reflexion über ElementInternals. Damit legt ein autonomes Custom Element (Web Component) seine Standardrolle für den Barrierefreiheitsbaum fest, zum Beispiel "button", "checkbox" oder "tablist". Screenreader und andere Hilfstechnologien sehen das Element dann mit dieser Rolle.

Der entscheidende Unterschied zum Attribut role bzw. zu Element.role: Der über ElementInternals gesetzte Wert ist ein interner Standardwert. Er taucht nicht als Attribut im Markup auf, und Nutzer der Komponente können ihn jederzeit mit einem eigenen role-Attribut am Host-Element überschreiben. So verhält sich die Komponente wie ein natives HTML-Element, das ebenfalls eine implizite Rolle hat (etwa <button>).

Typischerweise setzt man die Eigenschaft einmal im Konstruktor der Komponente, direkt nach this.attachInternals(). Dazu passende Zustände wie ariaChecked, ariaPressed oder ariaDisabled setzt man ebenfalls über das ElementInternals-Objekt.

Parameter

Name Typ Default Beschreibung
$value string | null null Beim Schreiben: eine gültige WAI-ARIA-Rolle wie "button", "switch" oder "listbox". Mit null entfernen Sie die Standardrolle wieder.

Rückgabewert

Typ
string | null
Beschreibung
Die aktuell gesetzte Standardrolle als Zeichenkette oder null, wenn keine gesetzt ist. Der Wert wird so zurückgegeben, wie er gesetzt wurde. Es findet keine Prüfung auf eine gültige Rolle statt.

Beispiele

Standardrolle im Konstruktor setzen

class FancyButton extends HTMLElement {
  constructor() {
    super();
    this.internals = this.attachInternals();
    // Implizite Rolle wie bei einem nativen <button>
    this.internals.role = 'button';
    this.tabIndex = 0;
  }
}
customElements.define('fancy-button', FancyButton);

const btn = document.createElement('fancy-button');
document.body.append(btn);
console.log(btn.internals.role);
console.log(btn.getAttribute('role'));
button null

Schalter mit Rolle und ARIA-Zustand

class ToggleSwitch extends HTMLElement {
  #internals;
  #on = false;

  constructor() {
    super();
    this.#internals = this.attachInternals();
    this.#internals.role = 'switch';
    this.#internals.ariaChecked = 'false';
    this.tabIndex = 0;
    this.addEventListener('click', () => this.toggle());
    this.addEventListener('keydown', (e) => {
      if (e.key === ' ' || e.key === 'Enter') {
        e.preventDefault();
        this.toggle();
      }
    });
  }

  toggle() {
    this.#on = !this.#on;
    this.#internals.ariaChecked = String(this.#on);
    console.log(`Zustand: ${this.#internals.ariaChecked}`);
  }
}
customElements.define('toggle-switch', ToggleSwitch);

const sw = document.createElement('toggle-switch');
document.body.append(sw);
sw.click();
Zustand: true

Autor überschreibt die Standardrolle per Attribut

class InfoCard extends HTMLElement {
  constructor() {
    super();
    this.internals = this.attachInternals();
    this.internals.role = 'region';
  }
}
customElements.define('info-card', InfoCard);

document.body.innerHTML = '<info-card role="note"></info-card>';
const card = document.querySelector('info-card');
// Interner Standardwert bleibt erhalten,
// Hilfstechnologien verwenden aber das Attribut "note"
console.log(card.internals.role, card.getAttribute('role'));
region note

Feature-Erkennung mit Fallback

class MyTab extends HTMLElement {
  constructor() {
    super();
    const internals = this.attachInternals?.();
    if (internals && 'role' in internals) {
      internals.role = 'tab';
    } else if (!this.hasAttribute('role')) {
      // Fallback für ältere Browser: sichtbares Attribut
      this.setAttribute('role', 'tab');
    }
  }
}
customElements.define('my-tab', MyTab);

// Wichtig · Fallstricke

  • Nur für Custom Elements: attachInternals() funktioniert nur bei autonomen Custom Elements. Bei eingebauten Elementen und bei angepassten eingebauten Elementen (is="…") wirft der Aufruf einen NotSupportedError. Außerdem lässt er sich pro Element nur einmal ausführen.
  • Keine Prüfung: Ungültige oder abstrakte Rollen (z. B. "widget") werden ohne Fehler gespeichert, aber von Hilfstechnologien ignoriert. Nutzen Sie nur konkrete Rollen aus der WAI-ARIA-Spezifikation.
  • Die Rolle ersetzt kein Verhalten: Wer role = 'button' setzt, muss Fokussierbarkeit (tabIndex) und Tastaturbedienung (Enter/Leertaste) selbst umsetzen.
  • Vorrang des Attributs: Ein role-Attribut am Host-Element hat immer Vorrang vor dem internen Wert. Das ist so gewollt und sollte nicht umgangen werden.
  • Nicht in CSS-Selektoren sichtbar: Da kein Attribut entsteht, greift [role="button"] nicht. Für das Styling bieten sich :host oder benutzerdefinierte Zustände (internals.states, :state()) an.
  • Kompatibilität: Firefox unterstützt die ARIA-Eigenschaften an ElementInternals erst ab Version 119. Für ältere Browser brauchen Sie eine Feature-Erkennung ('role' in internals) mit einem Attribut als Fallback.