Attributreflexion (Reflected Attributes) bezeichnet im DOM die automatische Synchronisation zwischen HTML-Content-Attributen im Markup und ihren korrespondierenden JavaScript-Eigenschaften (IDL-Attributen) auf dem Element-Objekt. Wer im Browser element.id, image.src oder input.disabled setzt, schreibt nicht in eine separate JavaScript-Eigenschaft, sondern verändert dasselbe zugrundeliegende Attribut, das auch über setAttribute() erreichbar ist. Diese auf den ersten Blick triviale Brücke ist überraschend regelreich: Sie kennt typisierte Konvertierungen, Standardwerte, URL-Auflösung relativ zur Basis-URL und Edge-Cases wie class vs. className oder for vs. htmlFor. Dieser Artikel erklärt das Konzept der Attributreflexion vollständig, beleuchtet die unterschiedlichen Reflexionstypen und zeigt anhand konkreter Beispiele, wann der Property-Zugriff dem Aufruf von getAttribute() überlegen ist - und wann nicht.

Was ist Attributreflexion?

Im DOM existiert für jedes HTML-Element ein Objekt, das die Eigenschaften und Methoden des Elements bereitstellt. Viele HTML-Attribute, die im Markup notiert werden, sind als Eigenschaften (Properties) auf diesem Objekt verfügbar. Diese Spiegelung wird als Attributreflexion bezeichnet. Das Content-Attribut (im HTML-Quelltext) und das IDL-Attribut (die JavaScript-Eigenschaft) verweisen dabei auf denselben internen Wert, sodass eine Änderung an einer Seite sofort auf der anderen sichtbar wird.

IDL steht für Interface Definition Language und beschreibt im Kontext der Web-Standards die formale Definition von Schnittstellen, die ein Element nach außen anbietet. Ein IDL-Attribut ist also nichts anderes als eine im Standard definierte Eigenschaft auf einem Element-Objekt - typischerweise mit Getter und Setter, die intern auf das Content-Attribut zugreifen.

HTML HTML-Markup
<img id="logo" src="/img/logo.png" alt="Logo">
JavaScript DOM-Zugriff
const img = document.getElementById("logo");

// Lesen über IDL-Attribut (Property)
img.id;        // "logo"
img.src;       // z.B. "https://example.com/img/logo.png"
img.alt;       // "Logo"

// Lesen über Content-Attribut
img.getAttribute("id");   // "logo"
img.getAttribute("src");  // "/img/logo.png"
img.getAttribute("alt");  // "Logo"

Bereits in diesem kurzen Beispiel zeigt sich ein wichtiger Unterschied: img.src liefert eine vollständig aufgelöste URL, während getAttribute("src") den im Markup geschriebenen Originalwert zurückgibt. Reflexion bedeutet eben nicht stur “gleicher String an beiden Enden” - viele Attribute werden bei der Reflexion typisiert und konvertiert.

Content-Attribute und IDL-Attribute

Um Attributreflexion sauber zu verstehen, muss man zwei Begriffe konsequent trennen:

  • Content-Attribute: Das Attribut, wie es im HTML-Markup steht. Es ist immer ein String und wird über die Methoden getAttribute(), setAttribute(), hasAttribute() und removeAttribute() manipuliert.
  • IDL-Attribut: Die JavaScript-Eigenschaft auf dem Element-Objekt, die auf das Content-Attribut “spiegelt”. Sie kann typisiert sein (Boolean, Number, String, URL, …) und besitzt häufig einen Standardwert.

Beide repräsentieren denselben Wert, jedoch in unterschiedlicher Form. Schreibt man in das eine, ändert sich auch das andere - mit den oben angedeuteten Konvertierungsregeln.

JavaScript Synchronisation
const input = document.querySelector("input");

// Content-Attribut setzen
input.setAttribute("value", "Hallo");
input.value; // "Hallo"

// IDL-Attribut setzen
input.id = "username";
input.getAttribute("id"); // "username"

Eine wichtige Ausnahme von dieser perfekten Spiegelung ist das value-Attribut von <input>-Elementen. Dort spiegelt das IDL-Attribut nur den Initialwert - sobald die Nutzerin tippt oder JavaScript input.value ändert, weichen Content- und IDL-Wert auseinander. Dieses Sonderverhalten ist im HTML-Standard explizit als “default value vs. current value” definiert.

Typen reflektierter Attribute

Der HTML-Standard definiert verschiedene Reflexionstypen. Welcher Typ konkret zur Anwendung kommt, hängt vom jeweiligen Attribut ab und steht in dessen IDL-Definition. Jeder Typ definiert Regeln dafür, wie aus dem String-Content-Attribut der typisierte Property-Wert wird - und umgekehrt.

String-Reflexion

Der einfachste Fall: Content-Attribut und IDL-Attribut enthalten denselben String. Beispiele sind id, title, lang oder name.

JavaScript String-Reflexion
const div = document.createElement("div");

div.id = "container";
div.getAttribute("id"); // "container"

div.setAttribute("title", "Tooltip-Text");
div.title; // "Tooltip-Text"

Boolean-Reflexion

Boolean-Attribute wie disabled, hidden, required oder readonly reflektieren auf eine boolean-Property. Entscheidend ist hier: Allein die Anwesenheit des Content-Attributs zählt - der Wert spielt keine Rolle. Auch disabled="" oder disabled="false" aktivieren das Boolean.

HTML HTML
<input disabled>
<input disabled="">
<input disabled="disabled">
<input disabled="false">  <!-- Trotzdem disabled! -->
JavaScript Boolean-Reflexion
const input = document.querySelector("input");

input.disabled;            // true
input.disabled = false;    // entfernt das Attribut
input.hasAttribute("disabled"); // false

input.disabled = true;     // fügt das Attribut wieder hinzu
input.getAttribute("disabled"); // "" (leerer String)

Setzt man das IDL-Attribut auf true, fügt der Browser das Content-Attribut mit leerem Wert hinzu. Setzt man es auf false, wird das Content-Attribut komplett entfernt. Genau aus diesem Grund ist element.disabled = false deutlich klarer als das fehleranfällige setAttribute("disabled", "false"), das das Element weiterhin deaktiviert lassen würde.

Enumerated-Reflexion

Enumerated-Attribute haben eine fest definierte Liste erlaubter Werte und einen Default-Wert, der greift, wenn das Attribut fehlt oder einen ungültigen Wert enthält. Beispiele sind contenteditable, dir, crossorigin oder loading.

HTML HTML
<img src="/foo.png" loading="lazy">
<img src="/bar.png">
<img src="/baz.png" loading="invalid-value">
JavaScript Enumerated-Reflexion
const [a, b, c] = document.querySelectorAll("img");

a.loading; // "lazy"
b.loading; // "eager" (Default, wenn Attribut fehlt)
c.loading; // "eager" (Default, weil Wert ungültig)

Hier wird der Mehrwert der Reflexion besonders deutlich: Statt selbst eine Liste valider Werte gegen getAttribute() zu vergleichen, liefert die Property bereits den kanonisierten Wert.

Numerische Reflexion

Numerische Attribute werden zu number oder unsigned long konvertiert. Beispiele sind tabindex, cols, rows, maxlength oder width und height an <img> und <canvas>.

JavaScript Numerische Reflexion
const textarea = document.createElement("textarea");

textarea.setAttribute("cols", "40");
textarea.cols;            // 40 (number, nicht "40")
typeof textarea.cols;     // "number"

textarea.rows = 10;
textarea.getAttribute("rows"); // "10" (immer String)

Beim Schreiben passiert die Umwandlung in die andere Richtung: Die Property akzeptiert eine Zahl, das Content-Attribut speichert deren Stringrepräsentation.

URL-Reflexion

Attribute, die URLs enthalten - etwa src, href, action, formaction oder poster - werden bei der Reflexion gegen die Basis-URL des Dokuments aufgelöst. Das Content-Attribut behält dabei den Originalwert.

HTML HTML auf https://example.com/page/
<a href="contact.html">Kontakt</a>
<a href="/about">Über uns</a>
<a href="https://other.example/x">Extern</a>
JavaScript URL-Reflexion
const [a, b, c] = document.querySelectorAll("a");

a.href; // "https://example.com/page/contact.html"
b.href; // "https://example.com/about"
c.href; // "https://other.example/x"

a.getAttribute("href"); // "contact.html"
b.getAttribute("href"); // "/about"
c.getAttribute("href"); // "https://other.example/x"

Wer ausschließlich den Originalwert benötigt - etwa um relative Pfade beizubehalten - sollte konsequent getAttribute() verwenden. Wer eine vollwertige absolute URL braucht, etwa für Vergleiche oder Fetch-Aufrufe, greift auf das IDL-Attribut zurück.

Reflexion mit alternativen Property-Namen

Einige HTML-Attributnamen sind in JavaScript reservierte Schlüsselwörter oder anderweitig problematisch. Aus Kompatibilitätsgründen gibt es deshalb für sie alternative IDL-Namen.

HTML-AttributIDL-Property
classclassName
forhtmlFor
<label> forhtmlFor
JavaScript className statt class
const div = document.createElement("div");

div.className = "card primary";
div.getAttribute("class"); // "card primary"

const label = document.createElement("label");
label.htmlFor = "username";
label.getAttribute("for"); // "username"

Diese Aliasse sind historisch bedingt - in frühen JavaScript-Versionen waren class und for reservierte Wörter und konnten nicht als Property-Namen genutzt werden. Heute funktioniert zwar obj["class"], der Standard hält jedoch aus Kompatibilitätsgründen an className und htmlFor fest.

Reflexion auf Objekte: DOMTokenList

Manche Attribute reflektieren nicht auf einen primitiven Typ, sondern auf ein DOMTokenList-Objekt. Das ist eine Live-Sammlung der einzelnen Tokens (durch Whitespace getrennt), die das Attribut enthält. Wichtige Beispiele sind class über classList, rel über relList und sandbox über sandbox an <iframe>.

JavaScript classList vs. className
const div = document.createElement("div");

div.className = "card";

// DOMTokenList bietet bequeme Methoden
div.classList.add("primary");
div.classList.add("large", "rounded");
div.classList.toggle("active");
div.classList.remove("large");
div.classList.contains("primary"); // true

div.className; // "card primary rounded active"
div.getAttribute("class"); // "card primary rounded active"

classList ist live - Änderungen am class-Attribut sind sofort in der Liste sichtbar. Im Gegensatz zu className, das eine String-Manipulation erzwingt (div.className += " extra" mit Risiko für doppelte Leerzeichen oder Mehrfacheinträge), bietet classList typsichere Operationen.

Praktischer Vergleich: Property vs. getAttribute()

Die Wahl zwischen direktem Property-Zugriff und getAttribute() ist nicht beliebig. Beide Wege haben klare Anwendungsfälle.

AufgabeEmpfohlenGrund
Boolean-Status lesen oder setzenProperty (el.disabled)Liefert echten Boolean, vermeidet "false"-Stringfalle
Numerischen Wert lesenProperty (el.tabIndex)Liefert Zahl, kein String-Cast nötig
Vollständig aufgelöste URLProperty (a.href)Browser löst gegen Basis-URL auf
Original-Markup-Wert einer URLgetAttribute("href")Behält relative Pfade
Daten-Attribute (data-*)datasetKomfortable Map-API
Custom-Attribute ohne ReflexiongetAttribute()Reflektiert nicht zuverlässig auf Property
Prüfen, ob ein Attribut vorhanden isthasAttribute()Property ist auch dann definiert, wenn Attribut fehlt
JavaScript Typische Stolperfalle
const input = document.querySelector("input[required]");

// Falsch: setAttribute mit "false" deaktiviert NICHT
input.setAttribute("required", "false");
input.required; // true (Attribut ist vorhanden)

// Richtig: Property verwenden
input.required = false;
input.hasAttribute("required"); // false

Custom Elements und Attributreflexion

Wer eigene Custom Elements baut, möchte häufig dasselbe Verhalten nachbauen: ein Attribut im Markup soll mit einer Property auf der Klasse synchronisiert sein. Der Standard sieht dafür zwei Bausteine vor:

  • observedAttributes definiert, welche Attribute beobachtet werden.
  • attributeChangedCallback wird aufgerufen, wenn sich ein beobachtetes Attribut ändert.
  • Setter und Getter auf der Klasse implementieren die Spiegelung in die Gegenrichtung.
JavaScript Reflexion in Custom Elements
class MyToggle extends HTMLElement {
    static get observedAttributes() {
        return ["open"];
    }

    get open() {
        return this.hasAttribute("open");
    }

    set open(value) {
        if (value) {
            this.setAttribute("open", "");
        } else {
            this.removeAttribute("open");
        }
    }

    attributeChangedCallback(name, oldValue, newValue) {
        if (name === "open") {
            this.dispatchEvent(new Event("toggle"));
        }
    }
}

customElements.define("my-toggle", MyToggle);

Mit diesem Muster verhält sich <my-toggle open> exakt wie native Boolean-Attribute: Über el.open = true/false wird das Markup synchron mitgeführt, und ein externer setAttribute()-Aufruf löst denselben Callback aus.

Seit der ElementInternals-API gibt es zusätzlich die Möglichkeit, an Form-Lifecycles und Validierung teilzunehmen, was die Reflexion komplexer Form-Attribute deutlich vereinfacht.

data-* Attribute und dataset

data-*-Attribute sind ein expliziter HTML-Standardmechanismus für eigene Daten. Sie werden nicht einzeln auf Properties gespiegelt, sondern gesammelt über die Property dataset als DOMStringMap bereitgestellt. Dabei werden Bindestrich-Schreibweisen automatisch in camelCase umgewandelt.

HTML HTML
<article
    id="post"
    data-post-id="42"
    data-author-name="Michael"
    data-published-at="2026-05-02">
</article>
JavaScript dataset
const post = document.getElementById("post");

post.dataset.postId;       // "42"
post.dataset.authorName;   // "Michael"
post.dataset.publishedAt;  // "2026-05-02"

// Schreiben aktualisiert das Content-Attribut
post.dataset.viewCount = "128";
post.getAttribute("data-view-count"); // "128"

// Löschen entfernt das Attribut
delete post.dataset.authorName;
post.hasAttribute("data-author-name"); // false

Werte sind immer Strings - dataset führt keine Typkonvertierung durch. Wer Zahlen oder JSON speichert, muss selbst parsen. Trotzdem ist dataset für Custom-Daten dem Roh-getAttribute() deutlich überlegen, weil die Map-API kompakter ist und der Standard die Namensgebung klar regelt.

Performance- und Konsistenzaspekte

Property-Zugriff ist in modernen Browsern in aller Regel schneller als getAttribute(), weil der Property-Pfad direkt auf die internen, typisierten Slots zugreift, während getAttribute() einen Lookup in der Attribut-Liste durchführt. In heißen Schleifen lohnt sich daher der Property-Zugriff:

JavaScript Schleifen-Optimierung
const items = document.querySelectorAll(".item");

// Langsamer
for (const el of items) {
    if (el.getAttribute("disabled") !== null) continue;
}

// Schneller, klarer
for (const el of items) {
    if (el.disabled) continue;
}

Konsistenz wiegt jedoch oft schwerer als Mikro-Performance: Wer in einem Code-Pfad immer getAttribute() und in einem anderen immer Properties verwendet, riskiert Bugs durch unterschiedliche Konvertierungsregeln (URL-Auflösung, Boolean-Edge-Cases, Default-Werte). Die Faustregel lautet daher: Properties bevorzugen - außer es geht explizit um den Originalwert eines Content-Attributs oder um ein nicht reflektiertes Custom-Attribut.

Zusammenfassung

Attributreflexion ist die Spiegelung zwischen HTML-Content-Attributen und JavaScript-IDL-Eigenschaften. Sie sieht oberflächlich trivial aus, hat aber eine ganze Reihe nützlicher Eigenheiten:

  • Typisierung: Properties liefern Boolean, Number oder aufgelöste URL statt rohem String.
  • Default-Werte: Enumerated-Attribute fallen bei fehlendem oder ungültigem Wert auf einen Standard zurück.
  • Boolean-Semantik: Allein die Anwesenheit des Content-Attributs zählt - Wertstrings wie "false" täuschen.
  • URL-Auflösung: href und src als Property liefern absolute URLs, getAttribute() den Originalwert.
  • Aliasse: className für class, htmlFor für for.
  • Sammlungen: classList, relList und dataset bieten komfortable APIs auf reflektierten Attributen.
  • Custom Elements: Reflexion lässt sich mit observedAttributes, attributeChangedCallback und Settern selbst nachbauen.

Wer diese Regeln im Kopf hat, vermeidet eine ganze Klasse subtiler DOM-Bugs und schreibt deutlich klareren Code.

/ Weiter

Zurück zu Document Object Model

Zur Übersicht