Start · Sprachen · JavaScript · Referenz · ElementInternals.ariaChecked

ElementInternals.ariaChecked

Eigenschaft

Liest oder setzt den Standardwert für <code>aria-checked</code> eines Custom Elements. So kennen assistive Technologien den Zustand „ausgewählt“, „nicht ausgewählt“ oder „gemischt“.

seit JavaScript Baseline 2023: Chrome/Edge 81+, Safari 16.4+, Fire Kategorie: webapi

Signatur

ElementInternals.ariaChecked

Beschreibung

Über ElementInternals.ariaChecked legt ein autonomes Custom Element (Web Component) seine Standard-Semantik für den ARIA-Zustand aria-checked fest. Der Zustand gehört zu Widgets, die man an- oder abwählen kann, zum Beispiel role="checkbox", role="radio", role="switch" oder role="menuitemcheckbox". Screenreader lesen den Wert vor, etwa „Kontrollkästchen, aktiviert“.

Der große Unterschied zu Element.ariaChecked oder setAttribute('aria-checked', …): Ein Wert, den man über ElementInternals setzt, erscheint nicht als Attribut im DOM des Host-Elements. Das Markup bleibt sauber, und die Komponente verwaltet ihren Zustand intern. Setzt ein Autor das Attribut aria-checked direkt am Element, überschreibt dieses Attribut den internen Standardwert.

Die Eigenschaft nimmt einen String an: "true", "false", "mixed" (unbestimmt, nur bei Checkboxen sinnvoll) oder "undefined". Mit null entfernt man den Standardwert. Das ElementInternals-Objekt bekommt man über this.attachInternals(), normalerweise im Konstruktor der Custom-Element-Klasse.

Parameter

Name Typ Default Beschreibung
$value (Setter) string | null null Neuer Zustand: "true", "false", "mixed" oder "undefined". Mit null wird der Standardwert entfernt.

Rückgabewert

Typ
string | null
Beschreibung
Der aktuell gesetzte Standardwert als String (zum Beispiel "true"). Wurde nichts gesetzt, ist der Wert null. Nicht-Strings wandelt der Setter in Strings um.

Beispiele

Barrierefreie Custom-Checkbox

class FancyCheckbox extends HTMLElement {
  #internals;
  #checked = false;

  constructor() {
    super();
    this.#internals = this.attachInternals();
    this.#internals.role = 'checkbox';
    this.#internals.ariaChecked = 'false';
  }

  connectedCallback() {
    if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
    this.addEventListener('click', () => this.toggle());
    this.addEventListener('keydown', (e) => {
      if (e.key === ' ') {
        e.preventDefault();
        this.toggle();
      }
    });
  }

  toggle() {
    this.#checked = !this.#checked;
    this.#internals.ariaChecked = String(this.#checked);
    this.#internals.states.add?.('checked');
    if (!this.#checked) this.#internals.states.delete?.('checked');
  }

  get checked() { return this.#checked; }
}

customElements.define('fancy-checkbox', FancyCheckbox);

const box = document.createElement('fancy-checkbox');
document.body.append(box);
box.click();
console.log(box.checked, box.getAttribute('aria-checked'));
true null

Dreifach-Zustand „mixed“ für eine „Alle auswählen“-Checkbox

class SelectAll extends HTMLElement {
  static observedAttributes = ['selected', 'total'];
  #internals = this.attachInternals();

  constructor() {
    super();
    this.#internals.role = 'checkbox';
    this.#internals.ariaLabel = 'Alle auswählen';
  }

  attributeChangedCallback() {
    const selected = Number(this.getAttribute('selected') ?? 0);
    const total = Number(this.getAttribute('total') ?? 0);
    this.#internals.ariaChecked =
      selected === 0 ? 'false' : selected === total ? 'true' : 'mixed';
  }

  get state() { return this.#internals.ariaChecked; }
}

customElements.define('select-all', SelectAll);

const el = document.createElement('select-all');
el.setAttribute('total', '5');
el.setAttribute('selected', '2');
console.log(el.state);
mixed

Autor-Attribut überschreibt den internen Standardwert

class ToggleSwitch extends HTMLElement {
  internals = this.attachInternals();
  constructor() {
    super();
    this.internals.role = 'switch';
    this.internals.ariaChecked = 'false';
  }
}
customElements.define('toggle-switch', ToggleSwitch);

const sw = document.createElement('toggle-switch');
// Das Attribut am Host hat Vorrang vor ElementInternals:
sw.setAttribute('aria-checked', 'true');

console.log(sw.internals.ariaChecked); // interner Standardwert
console.log(sw.ariaChecked);           // gespiegeltes Attribut
false true

// Wichtig · Fallstricke

  • Nur für autonome Custom Elements: attachInternals() funktioniert nicht bei eingebauten Elementen wie <div> und nicht bei „customized built-ins“ (is="…"). Sonst gibt es einen NotSupportedError. Pro Element darf man es nur einmal aufrufen.
  • Die Rolle ist Pflicht: ariaChecked wirkt nur bei einer passenden Rolle, zum Beispiel internals.role = 'checkbox'. Ohne Rolle ignorieren Screenreader den Wert.
  • Kein Attribut im DOM: Der Wert erscheint nicht als aria-checked am Host. CSS-Selektoren wie [aria-checked="true"] treffen deshalb nicht. Für das Styling eignen sich benutzerdefinierte Zustände über internals.states und :state(checked).
  • Nur Strings: internals.ariaChecked = true wird zu "true". Das klappt, aber String(value) ist klarer. Unbekannte Werte wie "yes" werden gespeichert, sind aber ungültiges ARIA.
  • Tastatur und Fokus fehlen: ARIA beschreibt nur den Zustand. Fokussierbarkeit (tabIndex) und Tastaturbedienung (Leertaste) muss die Komponente selbst umsetzen.
  • Kompatibilität: Die ARIA-Reflexion auf ElementInternals gibt es erst ab Chrome 81, Safari 16.4 und Firefox 119. Wer ältere Browser unterstützen muss, prüft vorher mit 'ariaChecked' in ElementInternals.prototype und greift sonst auf setAttribute zurück.