Signatur
Beschreibung
Mit ariaControlsElements auf einem ElementInternals-Objekt legt ein autonomes Custom Element fest, welche Elemente es steuert. Assistive Technologien wie Screenreader erfahren so zum Beispiel, dass ein eigener Aufklapp-Button ein bestimmtes Panel ein- und ausblendet oder dass ein eigenes Suchfeld eine Ergebnisliste aktualisiert.
Anders als das HTML-Attribut aria-controls braucht die Eigenschaft keine ID-Liste. Sie erwartet direkt ein Array von Element-Objekten. Das ist Element Reflection: Die Elemente brauchen keine id, und die Beziehung bleibt erhalten, auch wenn sich IDs ändern.
Über ElementInternals gesetzte Werte sind Standard-Semantik und im DOM unsichtbar. Es entsteht also kein Attribut am Host-Element. Setzt der Nutzer des Elements selbst aria-controls oder element.ariaControlsElements, hat dieser Wert Vorrang. So kann eine Komponente sinnvolle Vorgaben machen, ohne das Markup ihrer Nutzer zu überschreiben.
Das Objekt ElementInternals bekommt man nur einmal, über this.attachInternals() im Konstruktor des Custom Elements.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $value (Setter) | Array<Element> | null | null | Neue Liste der gesteuerten Elemente. null entfernt die Beziehung. Das Array muss immer als Ganzes neu zugewiesen werden. |
Rückgabewert
null, wenn nichts gesetzt ist. Elemente, die (noch) nicht erlaubt sind, etwa weil sie in einem nicht zugänglichen Shadow Tree liegen, fehlen im Ergebnis.Beispiele
Eigener Aufklapp-Button steuert ein Panel
class DisclosureButton extends HTMLElement {
#internals;
constructor() {
super();
this.#internals = this.attachInternals();
this.#internals.role = 'button';
this.#internals.ariaExpanded = 'false';
this.tabIndex = 0;
this.addEventListener('click', () => this.toggle());
}
connectedCallback() {
// Ziel-Panel über ein data-Attribut finden (liegt im Light DOM)
const panel = document.querySelector(this.dataset.target);
if (panel) {
this.#internals.ariaControlsElements = [panel];
}
}
toggle() {
const [panel] = this.#internals.ariaControlsElements ?? [];
if (!panel) return;
panel.hidden = !panel.hidden;
this.#internals.ariaExpanded = String(!panel.hidden);
}
}
customElements.define('disclosure-button', DisclosureButton);
// HTML:
// <disclosure-button data-target="#details">Details</disclosure-button>
// <div id="details" hidden>Versteckter Inhalt</div>
Mehrere gesteuerte Elemente setzen und auslesen
class FilterBox extends HTMLElement {
internals = this.attachInternals();
connectedCallback() {
this.internals.role = 'searchbox';
const list = document.getElementById('results');
const counter = document.getElementById('result-count');
// Immer ein neues Array zuweisen
this.internals.ariaControlsElements = [list, counter];
}
}
customElements.define('filter-box', FilterBox);
const box = document.querySelector('filter-box');
console.log(box.internals.ariaControlsElements.map((el) => el.id));
Beziehung entfernen und Fallstrick beim Mutieren
const internals = document.querySelector('filter-box').internals;
// FALSCH: Das zurückgegebene Array ist eingefroren
try {
internals.ariaControlsElements.push(document.body);
} catch (err) {
console.log(err instanceof TypeError);
}
// RICHTIG: neu zuweisen
internals.ariaControlsElements = [
...(internals.ariaControlsElements ?? []),
document.getElementById('extra')
];
// Beziehung komplett entfernen
internals.ariaControlsElements = null;
console.log(internals.ariaControlsElements);
// Wichtig · Fallstricke
- Browser-Unterstützung: Element Reflection für ARIA-Beziehungen ist relativ neu: Safari ab 16.4, Chrome/Edge ab 135, Firefox ab 136. Für ältere Browser braucht man einen Fallback, z. B.
aria-controlsmit IDs am Host setzen. Vorher prüfen mit'ariaControlsElements' in ElementInternals.prototype. - Grenzen des Shadow DOM: Erlaubt sind nur Elemente im selben Baum wie das Element oder in einem seiner übergeordneten Bäume, also zum Beispiel im Light DOM des Dokuments. Elemente im eigenen oder einem fremden, tiefer liegenden Shadow Root werden beim Auslesen herausgefiltert und für die Barrierefreiheit ignoriert.
- Vorrang: Ein
aria-controls-Attribut oderelement.ariaControlsElementsam Host überschreibt den Standardwert ausElementInternals. - Nicht mutieren: Der Getter liefert ein eingefrorenes Array. Änderungen gehen nur durch Neuzuweisen.
- Die Referenzen sind schwach: Wird ein Element aus dem DOM entfernt, fällt es aus dem Ergebnis heraus. Speicherlecks entstehen dadurch nicht.
aria-controlswird von vielen Screenreadern nur eingeschränkt angesagt. Deshalb zusätzlich Zustände wieariaExpandedpflegen.