Der Unterschied zwischen HTML-Attributen und DOM-Properties
(jakearchibald.com)- Auch wenn sie am selben Element hängen, verhalten sich HTML-Attribute und DOM-Properties wie getrennte Speicherbereiche; eine Änderung auf der einen Seite wird nicht unbedingt automatisch auf die andere übertragen
- Attribute erscheinen in der HTML-Serialisierung und im Elemente-Panel der Entwicklertools, Properties sind dagegen DOM-interner Zustand und tauchen in Ergebnissen wie
outerHTMLnicht auf - Attributwerte sind immer Strings, und ihre Namen sind nicht case-sensitiv; Properties können hingegen verschiedene Typen wie Objekte haben, und ihre Namen sind case-sensitiv
- Während es Properties gibt, die Attribute reflektieren, etwa
id,className,htmlForoderariaLabel, gibt es auch Ausnahmen wieinput.value, bei denen aktueller Zustand und Voreinstellung getrennt sind - Je nach Framework wird Unterschiedliches gesetzt: Preact und VueJS setzen meist Properties, React meist Attribute, und Lit unterscheidet beides explizit mit der
.value-Syntax
Attribute und Properties können trotz gleichen Namens verschieden sein
- Attribute und Properties können sich wie unterschiedliche Speicherbereiche verhalten
- Bei `` gibt
getAttribute('foo')den Wert'bar'zurück, währenddiv.fooundefinedsein kann - Selbst wenn danach
div.foo = 'hello world'gesetzt wird, bleibt der Wert desfoo-Attributs'bar'
- Bei `` gibt
- Auch wenn in Framework-Templates eine Syntax verwendet wird, die wie ein Attribut aussieht, etwa ``, kann intern je nach Fall eine Property gesetzt werden
- Wann eine Property gesetzt wird, unterscheidet sich je nach Framework
- Manche Zuweisungen können als Nebeneffekt sowohl Property als auch Attribut verändern
In der HTML-Serialisierung bleiben Attribute erhalten
- Attribute existieren sowohl in HTML als auch im DOM, Properties dagegen nur im DOM
- Selbst wenn sowohl
div.setAttribute('foo', 'bar')als auchdiv.hello = 'world'ausgeführt werden, erscheinen indiv.outerHTMLnur Attribute, etwa `
`
- Das Elemente-Panel der Browser-Entwicklertools zeigt die Attribute eines Elements und nicht die Gesamtheit seiner Properties
Werttypen und Groß-/Kleinschreibung
- Attributwerte müssen in serialisierter Form funktionieren und sind deshalb immer Strings
- Wenn ein Objekt mit
setAttributegesetzt wird, wird es als String"[object Object]"gespeichert - Wird dasselbe Objekt in eine Property geschrieben, kann der Objekttyp unverändert erhalten bleiben
- Wenn ein Objekt mit
- Attributnamen sind nicht case-sensitiv
HeLlO="world"kann im DOM zum Attributhellonormalisiert werden- Auch
setAttribute('FOO', 'bar')erscheint als Attributfoo
- Property-Namen sind case-sensitiv
- Selbst wenn
div.TeSt = 'value'gesetzt wird, kanndiv.testundefinedsein
- Selbst wenn
- Anders als Attributnamen sind Attributwerte case-sensitiv
Properties, die Attribute reflektieren
- Einige Properties reflektieren bestimmte Attribute
div.idverhält sich als Getter/Setter, der das Attributidliest und schreibt- Wird
div.id = 'bar'ausgeführt, ändert sich auch das Attributidzu'bar'
- In einer Reflexionsbeziehung ist das Attribut die Datenquelle
- Das Setzen der Property aktualisiert das Attribut
- Das Lesen der Property liest den Attributwert
- Für in der Spezifikation definierte Attribute werden zur Bequemlichkeit häufig entsprechende Properties angelegt
- Ein beliebiges Attribut
fooist kein in der Spezifikation definiertes Attribut, daher entsteht nicht automatisch eine entsprechende Propertyfoo
- Ein beliebiges Attribut
- Die ``-Spezifikation definiert Attribute unter „Content attributes“ und Properties unter „DOM interface“
- Die IDL-Attribute
reversedundtypemüssen die gleichnamigen Content-Attribute reflektieren
- Die IDL-Attribute
Reflexionsbeziehungen mit abweichenden Namen
- Manche Properties haben einen anderen Namen als das Attribut, das sie reflektieren
- Teils weicht der Name ab, um zum Stil von JavaScript-Properties zu passen
el.crossOriginvon `` reflektiert das Attributcrossoriginel.ariaLabelaller Elemente reflektiert das Attributaria-label- ARIA-Reflexions-Properties wurden Ende 2023 browserübergreifend verfügbar; davor konnten nur die Attribute verwendet werden
- In anderen Fällen änderte sich der Name wegen früherer reservierter Wörter in JavaScript
el.classNamealler Elemente reflektiert das Attributclassel.htmlForvon `` reflektiert das Attributfor
Validierung, Typumwandlung und Standardwerte
- Properties können Validierung und Standardwerte haben, Attribute selbst dagegen nicht
input.typezeigt den Unterschied zwischen Attributen und Properties gut- Selbst wenn
input.getAttribute('type')nullist, gibtinput.typeden Standardwert'text'zurück input.type = 'number'sieht sowohl im Attribut als auch in der Property wie'number'ausinput.type = 'foo'schreibt zwar'foo'ins Attribut, der Property-Getter erkennt aber den ungültigen Wert und gibt'text'zurück
- Selbst wenn
- Einige Properties führen Typumwandlungen aus
- Bei `` ist
details.getAttribute('open')ein leerer String unddetails.openisttrue details.open = falseentfernt das Attributopendetails.open = 'hello'schreibt den String nicht unverändert ins Attribut, sondern wandelt ihn in einen Boolean um, sodass das Attributopenvorhanden ist
- Bei `` ist
- Properties wie
img.heightwandeln Attributwerte in Zahlen um- Der Setter wandelt den eingehenden Wert in eine Zahl um; negative Werte werden als 0 behandelt
input.value trennt aktuellen Wert und Standardwert
- Bei
inputgibt es einevalue-Property und einvalue-Attribut, aber dievalue-Property reflektiert dasvalue-Attribut nicht - Die Property, die das
value-Attribut reflektiert, istdefaultValue- Der
defaultValue-Getter gibt dasvalue-Attribut oder einen leeren String zurück - Der
defaultValue-Setter setzt dasvalue-Attribut als String
- Der
- Die
value-Property folgt zunächstdefaultValue, verwendet aber einen internen Wert, sobald sie durch JavaScript oder Benutzerinteraktion gesetzt wurde- Bei anfänglichem `` sind
value,defaultValueundgetAttribute('value')alle'default' - Wird
input.defaultValue = 'new default'ausgeführt, sehen sowohl Attribut als auchvaluewie'new default'aus - Wird danach
input.value = 'hello!'ausgeführt, wirdvaluezu'hello!', während Attribut unddefaultValuebei'new default'bleiben - Auch wenn
setAttribute('value', 'another new default')ausgeführt wird, bleibt der aktuellevaluebei'hello!'
- Bei anfänglichem `` sind
- Wird das Formular zurückgesetzt, kann dies so funktionieren, dass der interne
value-Zustand initialisiert wird
Attribute passen natürlicher zu Konfiguration, Properties zu Zustand
- Die Unterscheidung, Attribute für Konfiguration und Properties für Zustand zu verwenden, ist natürlich
- `` ist, abgesehen vom Namen, ein Beispiel, das zu dieser Unterscheidung passt
- Das
value-Attribut legt den Standardwert fest - Die
value-Property stellt den aktuellen Zustand bereit
- Das
- Auch die Unterscheidung, dass beim Lesen oder Schreiben von Properties Validierung angewendet wird, beim Lesen oder Schreiben von Attributen jedoch nicht, passt zu diesem Modell
unddrücken ihren geöffneten Zustand mit dem Attributopenaus, und der Browser fügt dieses Attribut je nach Benutzerinteraktion direkt hinzu oder entfernt es- Dieses Design durchbricht die Unterscheidung, dass Attribute für Konfiguration gedacht sind
- Frameworks, die das DOM verwalten, oder gewöhnlicher JavaScript-Code müssen auf Situationen vorbereitet sein, in denen sich das DOM selbst ändert
- Ein alternatives Design könnte in ein Attribut für die Voreinstellung wie ``, eine Property
details.openzum Lesen und Schreiben des aktuellen Zustands und eine CSS-Pseudoklasse für Zustands-Targeting aufgeteilt sein - Simon Peters hat eine dazugehörige frühe Designdiskussion gefunden
- Auch
contenteditabledurchbricht diese Unterscheidung, wird aber als Opt-in behandelt, bei dem viele Brüche in Kauf genommen werden
Verarbeitung in verschiedenen Frameworks
- Preact und VueJS setzen, abgesehen von einigen vordefinierten Ausnahmen, eine Property, wenn
propName in elementwahr ist; andernfalls setzen sie ein Attribut- Standardmäßig bevorzugen sie Properties gegenüber Attributen
- String-Rendering-Methoden verhalten sich umgekehrt und ignorieren rein Property-basierte Einträge
- Zugehörige Implementierungen:
setPropertyin Preact,shouldSetAsPropin VueJS
- React setzt, abgesehen von einigen vordefinierten Ausnahmen, Attribute
- Dieser Ansatz führt zu einer Logik, die String-Rendering-Methoden ähnelt
- Properties von Custom Elements stehen nicht in Reacts vordefinierter Liste, werden daher als Attribute gesetzt, und rein Property-basierte APIs funktionieren unter Umständen nicht
- In React 19 soll für Custom Elements auf das Preact/VueJS-Modell umgestellt werden
- React hat den Property-Namen
classNamein einer attributähnlichen Syntax populär gemacht, setzt intern aber dasclass-Attribut - Zugehörige Implementierung:
setPropin React
- lit-html hält den Unterschied zwischen Attributen und Properties in der Syntax fest
- Wenn dem Namen wie in `` ein
.vorangestellt wird, wird nicht ein Attribut, sondern eine Property gesetzt - Zugehörige Dokumentation: Lit expressions
- Wenn dem Namen wie in `` ein
3 Kommentare
Hacker-News-Meinungen
data-*-Attribute sind ein guter Kompromiss zwischen den im Artikel behandelten Ansätzen.
Diese Attribute werden in JavaScript automatisch als les-/schreibbare Eigenschaften von
datasetverfügbar gemacht und können etwa so angesprochen werden:document.getElementById('myDiv').dataset.payload == "something".Man muss allerdings beachten, dass HTML-Attribute
kebab-caseverwenden, JavaScript dagegencamelCase; ausdata-my-cool-data-attributewird alsodataset.myCoolDataAttribute.In den verschiedenen Ökosystemen, die
camelCaseverwenden, gibt es keine etablierte Konvention dafür, wie Akronyme in Großbuchstaben behandelt werden sollen; Fälle wiedataset.myIDwerden dadurch uneindeutig.kebab-caseundcamelCasezu vermeiden, nutze ich Methoden wieElement.hasAttribute('data-thing'),Element.getAttribute('data-thing')undElement.setAttribute('data-thing', '...').Beim Prüfen oder Debuggen im DOM muss man dann keine merkwürdige Umwandlung mitdenken; die Absicht ist deutlich klarer.
Außerdem beschreibt https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att... das globale Attribut
data-*als „benutzerdefinierte Datenattribute, die es Skripten ermöglichen, eigene Informationen zwischen HTML und der DOM-Darstellung auszutauschen“.Mit Kontexten sind hier nicht unbedingt verschiedene
iframes gemeint, sondern Fälle, in denen — etwa bei Browser-Erweiterungen — separate JavaScript-Ausführungskontexte entstehen, die auf dasselbe DOM zugreifen.In der Terminologie des Developer-Tools-Protokolls nennt man so etwas auch isolated worlds.
Die im Artikel behandelten Dinge stehen für Komplexität und Verhalten, die im Leben kaum hilfreich sind.
Dieses Verhalten hat keine Vorteile, nur Nachteile; die Spezifikation hätte DOM-Objekte und JavaScript-Objekte einfach als dasselbe definieren und Attribute und Properties ebenfalls zum selben Konzept machen sollen.
Dass man auf DOM-Knoten nicht-String-Werte speichern kann, ist ein klarer Vorteil und nichts, was man zu leicht abtun sollte.
Es ist überhaupt nicht einfach, solange man nicht verlangt, dass alle Properties nur Strings sein dürfen.
Man muss Konvertierungen von und zu Zahlen, Properties, denen Objekte zugewiesen werden können, und Properties ohne entsprechendes Attribut berücksichtigen.
Trotzdem wäre es besser gewesen, wenn es zu 100 % feste Konventionen oder Regeln gegeben hätte — aber einfach ist das nicht.
Methoden wie
addEventListenersind Properties auf dem Prototyp einzelner DOM-Elemente, ergeben als String-Attribute aber keinen Sinn.Dann müssten DOM-Elementmethoden freie Funktionen wie
HTMLElement.addEventListener(foo, 'some-event', e => console.log(e));sein; das ist zwar Geschmackssache, aber in JavaScript ist es angenehmer, Objektmethodenaufrufe verwenden zu können.DOM-Elemente haben mit
setAttributeein sehr konkretes Interface, um Zustand zu ändern; wenn man an das JavaScript-Objekt, das dazu gedacht ist, Nachrichten an das DOM zu senden, einfach Properties anhängt, warum sollte man dann erwarten, dass das automatisch weitergeleitet wird?Eine solche API könnte eine bessere Erfahrung bieten, bringt aber einige schwierige Fragen mit sich; trotzdem wäre sie wohl sauberer.
Vielleicht liegt es daran, dass ich zu lange Backend-Code geschrieben habe, aber bei solchen Interfaces wirkt es auf mich, als passiere intern eine Menge Seltsames und als hätte ein freundlicher Entwickler aus der Vergangenheit den sichersten möglichen Wrapper bereitgestellt.
Eine hilfreiche Perspektive für das Verständnis ist, dass Attribute im engeren Sinne DOM-Knoten sind, nämlich
Attr-Knoten.Ein solcher Knoten kann Properties wie
name,value,ownerElementundprefixhaben und ist insgesamt nicht als Array, sondern alsNamedNodeMapüber die Propertyattributesdes besitzenden Elements zugänglich.Daher ist der Name
getAttribute(name)irreführend; tatsächlich gibt die Methode nicht den Attributknoten selbst zurück, sondern die Propertyvaluedieses Attributknotens, entspricht also ehergetAttributeValue(name).Siehe: https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Attr
setAttribute,getAttribute,getAttributeNamesoderhasAttribute, sondern nur über Attributknoten oderel.attributestun kann.Man kann Attributknoten weder zwischen Elementen verschieben noch die Reihenfolge der Attributknoten innerhalb eines Elements ändern.
Jake, was ist dann damit: https://jakearchibald.com/2023/against-self-closing-tags-in-...
Ich verstehe das Argument, Properties nicht zu aktualisieren, aber dass man sie per CSS gezielt ansprechen kann, ist nützlich
:checkedbei Eingabeelementen auch Pseudoklassen geben sollteEs ist viel einfacher zu debuggen, den DOM direkt zu überprüfen, als durch den Debugger zu gehen
Der vielleicht wichtigste Unterschied ist Persistenz, die im Artikel aber nicht behandelt wird
Attribute bleiben erhalten, solange man auf dasselbe Root-Objekt dieses DOM-Baums zugreifen kann, etwa solange man die Seite nicht verlässt
Objekt-Properties bleiben nur erhalten, bis das betreffende Objekt von der Garbage Collection erfasst wird
Wird der DOM-Knoten geklont oder serialisiert/deserialisiert, entsteht ein vollständig anderer DOM-Knoten mit denselben Attributen; er hat dann keine nicht standardmäßigen Properties eines anderen Knotens
Interessanter Artikel
Ich habe DOM-Elemente in JavaScript immer als eine Art Proxy gesehen
Sie lassen einen mit den eigentlichen DOM-Elementen arbeiten, sind aber gleichzeitig einfach JavaScript-Objekte; daher hatte ich das Gefühl, man könne neben den Funktionen, die mit dem echten Element interagieren, auch Werte zuweisen und tun, was man möchte
Mit Beispielen gut erklärt und angenehm zu lesen, besonders weil mehrere Details dabei sind, die leicht übersehen werden; das dürfte vielen helfen
DOM-Properties werden auch IDL attributes genannt
Außerhalb von Spezifikationsautoren nennt sie kaum jemand
IDL attributes, und wenn man beides Attribute nennt, wird es sehr verwirrendGuter Artikel, und er macht neugierig, wie eine bessere Ausdrucksweise aussehen könnte
Es wäre interessant, wenn HTML nicht-stringbasierte Attribute haben könnte und deren Werte etwa auf lokale Werte eines
script-Tags mit Scope verweisen würdenDie Sichtweise, Attribute als Grundeinstellungen zu sehen, fand ich etwas seltsam
Wenn zum Beispiel das
class-Attribut nur den Standardwert zeigen würde, wäre es ziemlich merkwürdig, wenn beim Umschalten von Klassen die aktive Klasse im DOM-Inspektor oder im stringifizierten HTML nicht sichtbar wäre oder eine inaktive Klasse angezeigt würdeDas
class-Attribut funktioniert in diesem System gutIch als Eigentümer des Dokuments setze das
class-Attribut und kann diese Einstellung im Laufe der Zeit ändern, aber nur ich tue dasIn einem alternativen System würde der Browser als Reaktion auf Hover- oder Focus-Zustände selbst Klassen hinzufügen und entfernen; glücklicherweise macht er das nicht, sondern es werden Pseudoklassen verwendet
https://emewjin.github.io/html-attribute-dom-property/
„Attribute“ und „Property“ wurden beide einfach als „Eigenschaft“ übersetzt …