Getippte CSS attr(): Direkte Eingabe von HTML-Attributwerten in Stylesheets
Erfahren Sie, wie die CSS-Funktion attr() Dateneigenschaften in Längen, Farben und Zeiten umwandelt, warum ihre Ersatzlösungen versagen, warum url() blockiert wird und wie Sie sie heute sicher einsetzen können.
Quellen: CSS attr() Reads Your HTML Now
Stellen Sie sich einen Fortschrittsbalken vor, dessen Wert 87 bereits im Markup als data-fill="87" enthalten ist. Bis vor Kurzem bedeutete es, diese Zahl in die Eigenschaft width einzubringen, den Einsatz eines kleinen Scripts: Man las dataset.fill aus, fügte ein Prozentsymbol hinzu und schrieb eine benutzerdefinierte Eigenschaft wieder auf dasselbe Element zurück, von dem die Zahl stammte. Der Wert verließ das Dokument nur, um direkt wieder hineingepusht zu werden – mit dem Nachteil eines Scripts und einer zusätzlichen Darstellungsschleife.
Durch die Verwendung von attr() entfällt dieser Umweg. Die Funktion akzeptiert nun einen Typ, sodass eine Stylesheet-Einstellung ein Attribut als Länge, Farbe, Zahl oder Identifikator lesen und es in jeder Eigenschaft verwenden kann, die diesen Typ zulässt. Diese Anleitung erläutert die Syntax, die vier möglichen Auflösungsweisen einer Abfrage, die Muster, mit denen häufige Scripts ersetzt werden können, die Sicherheitsbeschränkungen bei URLs sowie die Möglichkeit, diese Funktion trotz noch unvollständiger Browserunterstützung einzuführen.
Von String-Generator zu typisierter Wert
Die klassische Funktion attr() funktioniert seit etwa 2015 in allen gängigen Browsern, hatte aber genau eine Aufgabe: einen String für die content-Eigenschaft zu erzeugen, meist bei einem Pseudo-Element.
/* the version you already know */
blockquote::after {
content: " — " attr(cite);
}
Das ist nützlich, um eine Quelle oder ein Linkziel auszugeben – und nichts weiter. Wenn man die alte attr()-Funktion innerhalb von width verwendet, wird die gesamte Deklaration abgelehnt, weil die Funktion nur Strings zurückgibt und width keine Verwendung für einen String hat.
CSS Values and Units Level 5 ändert diese Regelung: Man gibt dem Browser an, welche Art von Wert das Attribut enthält. Sobald der Engine klar ist, dass es sich um eine Länge, eine Farbe oder eine Zahl handelt, kann der resultierende Wert überall verwendet werden, wo dieser Typ erlaubt ist – was in der Praxis fast jede Eigenschaft bedeutet.
/* the version this article is about */
.bar { width: attr(data-fill %, 0%); }
.chip { background-color: attr(data-brand type(<color>), #888); }
.dial { rotate: attr(data-deg deg, 0deg); }
.card { view-transition-name: attr(id type(<custom-ident>), none); }
Zwei Klarstellungen halten die Erwartungen realistisch, da diese Funktion in beide Richtungen leicht überbeworben werden kann.
- Es handelt sich dabei nicht um eine Methode, JavaScript aus der Styling-Logik zu entfernen. Das Einfügen von
style="--fill: 87%"direkt in das HTML hat niemals ein Skript erfordert. Das Neue ist eher begrenzt: Früher musste man für den Einsatz eines Wertes aus einem Attribut in einer Stylesheet ein Skript verwenden, jetzt ist das nicht mehr notwendig. - Dadurch wird die Anwendungslogik nicht in das CSS verlagert. Die Anwendung entscheidet weiterhin, dass die Aufgabe zu 87 Prozent abgeschlossen ist. Die einzige Veränderung besteht darin, wer diese Zahl aus dem Element liest: Ihr selbst geschriebener Code oder das Stylesheet, das ohnehin angewendet werden würde.
Anatomie der Funktion
Die Syntax passt in eine einzige Zeile.
attr( <attr-name> <attr-type>? , <fallback-value>? )
Nehmen Sie attr(data-fill %, 0%). Er besteht aus drei Teilen: dem Namen des Attributes, das gelesen werden soll, der Art, in der es interpretiert werden soll, sowie einem Ersatzwert für den Fall, dass die Abfrage kein verwendbares Ergebnis liefert.
Die Art kann in drei verschiedenen Formen angegeben werden. Die erste ist type(), wobei jeder CSS-Datentyp in spitze Klammern gesetzt wird.
/* 1. type() — any CSS data type */
color: attr(data-brand type(<color>), crimson);
width: attr(data-w type(<length>), 0px);
scale: attr(data-s type(<number>), 1);
Die zweite Form ist eine einfache Einheit, die angibt „das Attribut enthält eine reine Zahl; fügen Sie diese Einheit hinzu“. Die dritte Form ist raw-string, die ebenfalls Standard ist, wenn keine Art angegeben wird, und das historische Verhalten wiederholt.
/* 2. unit shorthand — the attribute holds a bare number */
width: attr(data-w px); /* data-w="10" → 10px */
rotate: attr(data-deg deg); /* data-deg="45" → 45deg */
animation-delay: attr(data-i s); /* data-i="2" → 2s */
width: attr(data-fill %); /* data-fill="87" → 87% *//* 3. raw-string — the default, and the old behaviour */
content: attr(data-name raw-string, "stranger");
content: attr(data-name); /* identical */
Die Einheitenabkürzung erwartet eine zahlenlose Zahl
attr(data-w px) bedeutet „lies die Zahl in data-w und betrachte sie als Pixel“. Das Attribut muss daher data-w="10" lauten. data-w="10px" wird nicht korrekt interpretiert, da die Einheit von der Stylesheet festgelegt wird und nicht vom Markup. Wenn Sie möchten, dass das HTML seine eigene Einheit enthält (zum Beispiel weil ein Editor 2rem oder 10px eingeben kann), wechseln Sie zu type(<length>) und speichern Sie den vollständigen Wert.
Ein ungültiger Ersatzversuch scheitert früh und auf andere Weise
Der Fallback wird zusammen mit dem Rest der Deklaration解析t, noch bevor der Browser das Element überhaupt untersucht. Wenn Sie attr(data-w px, banana) schreiben, ist die Deklaration zum Zeitpunkt der Parsing ungültig und wird vollständig ignoriert: Das Attribut wird niemals gelesen, und stattdessen gilt eine frühere Deklaration derselben Eigenschaft in der Regel. Das ist ein völlig anderes Versagen als das Fehlen eines Attributs, welches erst viel später bei der Berechnung des Wertes entdeckt wird. Wenn Sie diese beiden Fälle getrennt betrachten, fällt das Debuggen deutlich leichter.
Die Typen, die Sie deklarieren können
Innerhalb von type() werden die folgenden Typen akzeptiert, zusammen mit dem Inhalt, den das Attribut enthalten sollte, sowie typischen Anwendungsbereichen für jeden Typ.
<length>: Werte wie"12px"oder"2rem"fürwidth,paddingoderinset.
<percentage>: "87%", für width oder translate.<length-percentage>: eine der beiden vorherigen Werte, für alles, was eine Größe benötigt.<number>: "4.5", für opacity, scale oder als Eingabe für calc().<integer>: "3", für grid-column, z-index oder order.<color>: "#4fc3f7" oder "tomato", für color und background-color.<angle>: "45deg" oder "0.25turn", für rotate oder Gradientwinkel.<time>: "300ms", für animation-delay und transition-duration.<resolution>: eine Dichte wie "2dppx", die von image-resolution verwendet wird.<custom-ident>: ein Identifikator wie "card-3", nützlich zum Benennen von Elementen wie view-transition-name oder anchor-name.<string>: ein in Anführungszeichen gesetzter CSS-String, typischerweise von content verwendet.<transform-function>: etwas wie "rotate(20deg)", das mit der transform-Eigenschaft verwendet wird.<image> und <url>: vorhanden in der Grammatik, aber aus den im folgenden Sicherheitsabschnitt erläuterten Gründen faktisch nicht nutzbar.*: akzeptiert alles und ist hauptsächlich zum Erkennen von Funktionen nützlich.Zahlen benötigen calc(), um zu Größen zu werden
Ein <number> ist dimensionslos. 4.5 kann allein keine Breite festlegen, weshalb es fast immer innerhalb von calc() verwendet wird. Der übersichtlichste Ansatz ist es, die Zahl zunächst in einer benutzerdefinierten Eigenschaft zu speichern und dort die Berechnung durchzuführen.
.stars {
--r: attr(data-rate type(<number>), 0);
width: calc(var(--r) * 20%); /* 4.5 → 90% */
}
Der Zwischewert --r dient nicht nur der Formalität. Er ermöglicht es, den Wert in mehreren Deklarationen wiederverwenden zu können. Da benutzerdefinierte Eigenschaften vererbt werden, ist dies auch der Weg, über den der Wert an Nachkommen weitergegeben wird – was, wie Sie sehen werden, die einzige Möglichkeit ist, ein Attribut mit Kindenelementen zu teilen. Wenn Sie bereits benutzerdefinierte Eigenschaften als Übertragungsmittel zwischen Skripten und Styles verwenden, behandelt der Artikel zu benutzerdefinierten Eigenschaften zur Laufzeit die andere Seite dieser Brücke.
Wie eine Abfrage gelöst wird: vier Ergebnisse, nicht zwei
Es ist verlockend anzunehmen, dass die Funktion entweder funktioniert oder die Regel ignoriert wird. Tatsächlich gibt es vier unterschiedliche Ergebnisse, und deren Verwechslung ist die häufigste Ursache für Überraschungen.
- Das Attribut fehlt. Wenn ein Ersatzwert angegeben ist, wird dieser verwendet. Andernfalls gilt der Wert „garantiert ungültig“ und die Deklaration wird zum Zeitpunkt des Berechnungswerts (IACVT) ungültig.
- Das Attribut existiert, ist aber leer (
data-x=""). Bei einer typisiertenattr()-Aufrufung wird eine leere Zeichenkette nicht als Typ interpretiert, weshalb der Ersatzwert angewendet wird. Ein untypisierter Aufruf hingegen gilt als erfolgreich und liefert eine leere Zeichenkette, wodurch der Ersatzwert übersprungen wird. - Der Wert lässt sich nicht als der deklarierte Typ interpretieren. Wenn ein Ersatzwert vorhanden ist, wird dieser verwendet; andernfalls gilt IACVT.
- Der Wert lässt sich interpretieren. Der Wert wird verwendet und die Schriftart aktualisiert sich sofort, sobald sich das Attribut ändert.
Was „ungültig zum Zeitpunkt des Berechnungswerts“ wirklich bedeutet
Während der Parsung wird ein regulärer Syntaxfehler erkannt. Der Browser ignoriert diese Deklaration, und eine frühere Deklaration derselben Eigenschaft in derselben Regel tritt in Kraft. IACVT tritt viel später auf, nachdem die Kaskade bereits eine gültige Deklaration ausgewählt hat, sodass es nichts mehr gibt, auf das man zurückgreifen kann. Die Eigenschaft verhält sich dann, als wäre sie auf unset gesetzt: Vererbte Eigenschaften erhalten den Wert des Elternteils, und nicht vererbte Eigenschaften kehren zu ihrem ursprünglichen Wert zurück.
.bar {
width: 50%; /* cascade drops this: the next one parsed fine */
width: attr(data-fill %); /* attribute missing → IACVT → width: auto */
}
/* result: auto. NOT 50%. */
Deshalb ist es keine Frage des Geschmacks, stets einen Ersatz bereitzustellen. Ohne diesen führt ein falsch geschriebener Attributname stillschweigend zu auto, 0 oder einer vererbten Farbe, und die Konsole bleibt stumm.
Warum ein untypierter attr() keine Farbe erzeugen kann
Die Standard-Einstellung raw-string verdient besondere Aufmerksamkeit. Angenommen, eine Regel legt background-color aus einem Attribut ohne Typ fest, und Sie testen sie an Elementen, die sich nur in diesem Attribut unterscheiden. Das Element ohne das Attribut erhält wie erwartet den Ersatzwert. Jedes Element, das das Attribut besitzt, wird durchsichtig dargestellt – einschließlich solcher, die einen völlig gültigen Farbnamen enthalten.
Die Erklärung ist, dass eine untypisierte attr()-Funktion den literalen Text des Attributs als CSS-String ersetzt, anstatt einen parsierten CSS-Wert. background-color akzeptiert keinen String, weshalb die Ersetzung zur Zeit der Berechnung des Werts fehlschlägt. Der Fallback hilft nicht: Er bezieht sich nur auf fehlende Attribute oder Parsing-Fehler bei einem deklarierten Typ, und in dieser Regel wurde nichts deklariert. Die Abfrage war erfolgreich, da ein String zurückgegeben werden konnte; sie scheiterte jedoch einen Schritt später daran, dass es sich um eine Farbe handeln sollte, wobei nichts vorhanden ist, um dies abzufangen.
/* data-c="red" — and the element still renders transparent */
.swatch { background-color: attr(data-c, gold); }
Die Schlussfolgerung ist einfach: Immer dann, wenn die Ziel-Eigenschaft nicht content ist, muss ein Typ deklariert werden.
Muster zur Ersetzung eines Skripts
Mehrere Dinge, die Teams üblicherweise mit ein paar Zeilen JavaScript erledigen, können nun vollständig in der Stylesheet-Datei abgebildet werden.
Breite aus einem Datenattribut
Die Markup-Struktur enthält eine einfache Zahl.
<div class="bar" data-fill="87"></div>
Die Stylesheet liest den Wert mit der Prozent-Schreibweise und fällt bei Fehlern auf einen leeren Strich zurück.
.bar {
height: 8px;
border-radius: 99px;
background: linear-gradient(90deg, #4fc3f7, #2dd4bf);
width: attr(data-fill %, 0%);
}
Eine in einem CMS ausgewählte Farbe
.chip { background-color: attr(data-brand type(<color>), #888); }
Eine von einem Inhaltseditor ausgewählte Markenfarbe ist eher Daten als Design. Mit einer typisierten attr()-Funktion kann sie direkt das Stylesheet steuern, ohne inline-Styles in das Template einzufügen. Da die Validierung von <color> im Browser stattfindet, führt ein Tippfehler des Editors einfach zum grauen Fallback anstelle von fehlerhaftem CSS.
Animierungen für versetzte Listen
Eine versetzte Anzeige wird in der Regel durch das Setzen eines inline-Styles innerhalb einer Render-Schleife erzeugt. Der Index ist jedoch bereits Teil der Markup-Struktur und muss nicht in eine Style-Eigenschaft umgewandelt werden.
<li data-i="0">Inbox</li>
<li data-i="1">Drafts</li>
<li data-i="2">Sent</li>
Jeder Eintrag liest seinen eigenen Index als Zahl und calc() wandelt diesen in eine Verzögerung um.
li {
--i: attr(data-i type(<number>), 0);
animation: fade-in .45s both;
animation-delay: calc(var(--i) * .09s);
}
Falls das Attribut bereits eine vollständige Dauer enthält, kann die Abkürzung s diese direkt verwenden.
/* or, if the attribute already holds a duration */
li { animation-delay: attr(data-delay s, 0s); }
Durch die Übermittlung des Index über --i entsteht ein zusätzlicher Vorteil: Der Zeitabstand wird zu einer einzigen CSS-Konstante. Ändert man .09s an einer Stelle, übernimmt jede gestaffelte Liste auf der Seite den neuen Rhythmus.
Ein einzigartiger view-transition-name pro Karte
Dieses Muster ermöglicht etwas, was zuvor nicht möglich war. Jedes Element, das an einer Ansichtsübergangs-Operation teilnimmt, muss seinen eigenen einzigartigen view-transition-name haben – was früher bedeutete, dass für jede Karte eine eigene Regel erstellt oder Namen aus einem Skript zugewiesen werden mussten. Jetzt wird jede Karte durch eine einzige Regel gesteuert.
.card {
view-transition-name: attr(id type(<custom-ident>), none);
view-transition-class: card;
}
Bemerken Sie, dass das Attribut hier id ist und kein data-*-Attribut. attr() kann jedes Attribut des Elements lesen, einschließlich id, href, title, colspan oder lang. Der Namespace data-* dient lediglich dazu, dass HTML erwartet, dass benutzerdefinierte Seitendaten dort gespeichert werden. Die Kombination des Namens mit view-transition-class ermöglicht es Ihnen, die Übergänge aller Karten mit einem einzigen Selektor zu stylen, obwohl jede einen eindeutigen Namen hat.
Eine nützliche Methode, um diese Muster zu überprüfen, besteht darin, die basierende Skriptlösung vorübergehend zu entfernen und zu prüfen, ob die Stylinge weiterhin sichtbar sind; wenn dem so ist, erledigt attr() die Arbeit.
Pseudo-Elemente: Der Fall ohne Workaround
Jeder andere Vorteil in diesem Leitfaden hat eine Alternative: Man kann immer eine benutzerdefinierte Eigenschaft inline festlegen. Pseudo-Elemente sind anders. element.style bezieht sich auf das Element selbst, und ::before oder ::after sind keine DOM-Node, sodass es nichts gibt, woran man einen inline-Stil anhängen könnte. Die vorherigen Optionen waren eine vom Host geerbte benutzerdefinierte Eigenschaft (was dennoch eine Art Styling des Hosts erfordert) oder ein generierter Stylesheet. Ein Attribut am Host hingegen ist von beiden Pseudo-Elementen direkt abrufbar.
Hier ist ein Meter, dessen einzige Daten ein einzelnes Attribut sind.
<div class="meter" data-pct="72"></div>
Die Beschriftung verwendet die klassische Funktion attr() im content, die in jedem Browser funktioniert.
.meter::before {
content: attr(data-pct) "% complete";
}
Der Balken verwendet die typisierte Form, um seine Breite aus demselben Attribut zu setzen.
.meter::after {
content: "";
display: block;
height: 10px;
width: attr(data-pct %, 0%);
background: linear-gradient(90deg, #fbbf24, #f472b6);
}
Ein Attribut versorgt zwei Pseudo-Elemente, und keines davon benötigt ein zusätzliches Kindelement. Zählzeichen, Fortschrittsringe, Tooltip-Texte, Ordnungspräfixe, Einheitssuffixe sowie Datenetiketten von Diagrammen folgen alle diesem Muster: ein Pseudo-Element, das eine Zahl benötigt, die bereits im Host-Element vorhanden ist. Bevor attr() eingeführt wurde, musste diese Zahl zweimal eingegeben werden – einmal als Attribut für den Text und einmal als benutzerdefinierte Eigenschaft für die Geometrie – wodurch sich die beiden Versionen unterscheiden konnten. Heute wird sie nur noch einmal eingegeben.
attr() liest nur das gestylte Element
Die Funktion liest Attribute von dem Element, auf das die Regel zutrifft. Sie betrachtet weder Vorfahren noch Geschwisterelemente, und es gibt kein Äquivalent zu closest(). Da ein Fallback aktiviert wird, scheitert dieser Fehler stillschweigend – weshalb fast jeder ihn mindestens einmal macht.
Betrachten Sie einen Abschnitt, der eine Akzentfarbe für seinen Inhalt deklariert.
<section class="theme" data-accent="#4fc3f7">
<h2>Title</h2>
</section>
Das Lesen des Attributs aus dem Überschriftselement bringt nichts Nützliches, weil das Element dieses Attribut nicht besitzt.
/* WRONG — this reads the h2, which has no data-accent */
.theme h2 { color: attr(data-accent type(<color>), #888); }
/* every heading is #888, forever, with no error */
Die Lösung besteht darin, das Attribut einmal am Element zu lesen, das es enthält, und den Wert über eine benannte Eigenschaft weiterzugeben.
/* RIGHT — read it once on the element that has it, then inherit */
.theme { --accent: attr(data-accent type(<color>), #888); }
.theme h2 { color: var(--accent); }
.theme a { border-bottom: 2px solid var(--accent); }
Durch Vererbung erfolgt die Verteilung. Als Bonus kann nun jedes Unterbaumelement den Akzent mit einer gewöhnlichen --accent-Deklaration überschreiben, was der direkte attr()-Ansatz niemals ermöglichen würde.
Warum URLs tabu sind
Jeder von attr() erzeugte Wert wird als attr()-tainted markiert, und ein solcher verunreinigter Wert darf niemals zur Erstellung einer URL verwendet werden, egal für welchen Pfad. Beide Deklarationen haben keinerlei Wirkung:
span[data-icon] { background-image: url(attr(data-icon)); }
span[data-icon] { background: image-set(attr(data-icon)); }
Die Verunreinigung wird zusammen mit dem Wert übertragen. Egal ob sie über eine benannte Eigenschaft geleitet wird, in src() oder image() eingebettet ist oder in anderen Funktionen beliebiger Tiefe verschachtelt wird – der Einfluss bleibt bestehen. Wo auch immer sie in einem URL-Kontext landet, wird die Deklaration zum Zeitpunkt der Berechnung des Werts ungültig.
Das Bedrohungsmodell erläutert die Regel. Die Werte der Attribute werden oft von Benutzern beeinflusst: Anzeigennamen, CMS-Slug-Bezeichnungen, Kommentarüberschriften. Wenn eine Stylesheet-Datei einen solchen Wert in eine Netzwerkanfrage umwandeln könnte, könnte jeder, der in dieses Feld schreiben kann, den Browser jedes Besuchers dazu bringen, eine von ihm gewählte Adresse sowie alles, was der Browser normalerweise zu solchen Anfragen hinzufügt, abzurufen. Die Lösung dieses Problems in der Spezifikation ermöglichte es den Browserherstellern, sich darauf einzigen, diese Funktion auszuliefern. Die Ankündigung von WebKit auf Interop 2026 macht diesen Zusammenhang explizit: Da die Sicherheitsfragen in der Spezifikation geklärt sind, gaben alle Browseranbieter an, bereit zu sein, diese Fähigkeit auszuliefern und dies auf interoperable Weise zu tun.
Behalten Sie die Grenzen dieser Schutzmaßnahme im Hinterkopf. Alles, was in einem Attribut steht, ist für jede Skript auf der Seite lesbar, im View-Source sichtbar und wird im HTML-Payload übermittelt. Die URL-Beschränkung schließt einen Weg zur Datenentnahme; sie macht die Attribute jedoch nicht privat. Legen Sie niemals Geheimnisse hinein.
Progressive Erkennung von Unterstützung und Lieferung
Der sichere Ansatz besteht aus einer einfachen Grundlage, die jeder Browser versteht, wobei die typisierte Version für Engines hinzugefügt wird, die sie unterstützen. Beginnen Sie mit der Grundlage, hier gesteuert durch eine inline-Spezialeigenschaft.
.bar {
width: var(--fill, 0%); /* baseline, everyone */
}
Lassen Sie anschließend die unterstützenden Browser auf das Attribut wechseln.
@supports (width: attr(x %)) {
.bar {
width: attr(data-fill %, 0%); /* modern browsers take over */
}
}
Derselbe Test steht zur Laufzeit zur Verfügung, falls Ihr Code bedingte Ausführungen benötigt.
// the same answer at runtime, if you need to branch in code
if (CSS.supports("width", "attr(x %)")) {
// typed attr() is available
}
Test gegen eine echte Eigenschaft
Achten Sie auf Detection-Snippets, die einen Platzhalter für den Eigennamen verwenden, wie zum Beispiel @supports (x: attr(x type(*))), der in Referenzdokumentationen vorkommt. Es gibt keine CSS-Eigenschaft mit dem Namen x, und jeder Browser antwortet mit „false“, wenn eine Unterstützungsabfrage auf eine Eigenschaft verweist, die er nicht kennt – daher scheitert der Test auch in Browsern, in denen die Funktion funktioniert. Verwenden Sie stattdessen eine tatsächlich existierende Eigenschaft. Die Abfrage @supports (width: attr(x %)) ist zuverlässig: width ist eine echte Eigenschaft, und attr(x %) kann nur von Engines interpretiert werden, die die Abkürzung für Einheiten implementieren. Es lohnt sich, die aktuelle MDN-Dokumentation zu attr() zu prüfen, da Beispiel in der Dokumentation gelegentlich korrigiert werden.
Ausnutzung der Kaskade anstelle von @supports
Eine kürzere Variante überspringt den @supports-Block und lässt die Kaskade entscheiden.
.bar {
width: var(--fill, 0%); /* everyone */
width: attr(data-fill %, 0%); /* supporting browsers override */
}
Ein Browser, der attr() innerhalb von width nicht parsen kann, ignoriert diese Deklaration beim Parsen, sodass die vorherige Zeile gilt. Dieses Verhalten ergibt sich aus der Art und Weise, wie die Kaskade ungültige Deklarationen behandelt. Dennoch macht die @supports-Formulierung die Absicht explizit und kostet nur ein paar zusätzliche Zeilen – weshalb man sie bevorzugen sollte, wenn man möchte, dass das Verhalten für zukünftige Leser offensichtlich ist. Prüfen Sie die kürzere Form in einem nicht unterstützenden Browser, bevor Sie sich darauf verlassen.
In beiden Varianten sollte man einen Fallback innerhalb von attr() selbst bereitstellen. In einem unterstützenden Browser führt das Fehlen eines Attributs nicht zu einem Rückgriff auf die obige var()-Deklaration; die attr()-Deklaration hat bereits in der Kaskade gewonnen, wodurch die Breite auf auto gesetzt wird.
Browserunterstützung zum Zeitpunkt der Erstellung
Die Unterstützung ändert sich schnell, daher sollten Sie diese Zahlen als Momentaufnahme betrachten und sie vor dem Vertrauen darauf in Can I use überprüfen. Stand August 2026 verfügten etwa 70 Prozent der weltweiten Nutzer über einen Browser mit der Funktion attr():
- Chrome 133 und neuer: Unterstützung vorhanden, erste Version mit dieser Funktion (siehe Ankündigung zu Chrome for Developers).
- Edge 133 und neuer: Unterstützung vorhanden, folgt der Veröffentlichung von Chromium.
- Firefox 155 und neuer: Unterstützung vorhanden; in den Versionen 152 bis 154 vorhanden, aber standardmäßig deaktiviert.
- Samsung Internet 29 und neuer: Unterstützung vorhanden.
- Safari auf Desktop: Noch nicht verfügbar; nur in der Technology Preview erhältlich.
- Safari auf iOS: Noch nicht verfügbar, und da alle iOS-Browser WebKit verwenden, gilt dies für alle iOS-Browser.
Der veraltete, ausschließlich auf Strings basierende attr()-Funktion innerhalb von content ist eine separate Angelegenheit. Er steht bereits seit einem Jahrzehnt in Form von Baseline weit verbreitet zur Verfügung, bereits seit den sehr frühen Versionen jeder großen Engine. Wenn Sie nur content: attr(data-label) benötigen, können Sie ihn ohne die oben genannten Vorsichtsmaßnahmen verwenden.
Interop 2026 listet den typisierten attr()-Aufruf unter seinen Schwerpunktthemen auf. Interop ist eine jährliche Zusammenarbeit, bei der Apple, Google, Microsoft und Mozilla zusammen mit Igalia und Bocoup Funktionen auswählen, die in mindestens einem Browser fehlen oder nicht richtig funktionieren, und sich verpflichten, sie im Laufe des Jahres zu verbessern. Der Fortschritt wird anhand der gemeinsam genutzten Web Platform Tests auf einer öffentlichen Anzeige überwacht. Das ist das stärkste öffentliche Zeichen dafür, dass die Unterstützung für Safari entwickelt wird – es handelt sich jedoch um ein Versprechen, nicht um einen Veröffentlichungstermin. Planen Sie für den Rest des Jahres 2026 eine Alternative ein. Für einen umfassenderen Ansatz bei der Bereitstellung neuer CSS-Funktionen innerhalb sicherer Grundlagen siehe sichere Bereitstellung moderner CSS.
Wahl zwischen attr() und JavaScript
Auch eines der Tools ist nicht überall vorteilhaft. Die folgende Einteilung dient als praktischer Leitfaden.
Wählen Sie attr(), wenn:
- Die Markup-Struktur enthält bereits den Wert. Den Wert in ein Skript zu übernehmen, nur um ihn am selben Knoten erneut einzustellen, bringt nichts.
- Er muss direkt im ersten Frame vorhanden sein. Das Attribut existiert bereits, sobald der Parser das Element erstellt, während ein Skript später ausgeführt wird und zu sichtbaren Korrekturen führen kann.
- Ein Pseudo-Element benötigt den Wert. Inline-Stile, die über
element.stylegesetzt werden, können::beforeoder::afternicht ansprechen. - Ihre Content Security Policy ist streng. Inline-Stile erfordern
unsafe-inlineoder ein nonce; Datenattribute benötigen keines davon.
MutationObserver erforderlich, um den Baum zu überwachen.Bleiben Sie bei JavaScript, wenn:
- Der Wert keinen Grund hat, im DOM zu existieren. Eine Messung aus einem
ResizeObserveroder einem Scroll-Handler sollte nicht zunächst in ein Attribut umgewandelt werden. - Es aktualisiert jeden Animationsschritt. Die Aufrufung von
el.style.setProperty('--x', v)ist direkter und erspart das Schreiben eines Attributs. - Die übertragenen Daten sind umfangreich, verschachtelt oder vertraulich. Alles, was in einem Attribut steht, ist eine öffentlich sichtbare Zeichenkette.
- Man benötigt heute Safari und der Wert ist wichtig. Ein Fortschrittsbalken, der in einem Browser bei null stecken bleibt, ist ein Defekt – kein eleganter Ersatz.
In der ersten Liste fehlt die Leistungsfähigkeit völlig. Das Setzen eines inline-Styles war nie ein Engpass. Man gewinnt eine bessere Struktur für den Wert sowie bessere Sichtbarkeit darauf – nicht Geschwindigkeit.
Checkliste für Fallstricke
- Bieten Sie immer einen Ersatz an. Ein fehlender oder unverarbeitbarer Wert ohne Ersatz führt zu IACVT sowie einer
unset-Eigenschaft – nicht zur vorherigen Deklaration. - Ein leeres Attribut ist kein fehlendes Attribut. Wenn es nicht typisiert ist, gibt
data-x=""einen leeren String zurück und der Fallback tritt niemals in Kraft. - Nicht typisiert bedeutet literaler Text.
background-color: attr(data-c, gold)mitdata-c="red"zeigt nichts an; fügen Sie einen Typ hinzu. - Kein
url()und keinimage-set(). Die Problematik ist absichtlich und bleibt auch bei benutzerdefinierten Eigenschaften bestehen. - Nur das abgegliche Element wird ausgelesen. Für Vorfahren sollten Sie den Wert in einer benutzerdefinierten Eigenschaft speichern, damit er vererbt wird.
- Für Einheitenkürzel sind reine Zahlen erforderlich. Verwenden Sie
data-w="10", nichtdata-w="10px". - Ein ungültiger Fallback führt zur Abbruch der Deklaration bereits bei der Parsing-Zeit, was ein früherer und anderer Fehler ist als das Fehlen eines Attributs.
- Ein
<number>kann alleine nichts in Größe versetzen. Speichern Sie ihn und multiplizieren Sie ihn incalc(). - Attribute sind öffentlich. Legen Sie keine Geheimnisse hinein.
- Testen Sie die Funktion mit einer echten Eigenschaft, wie z. B.
@supports (width: attr(x %)). - Zum Zeitpunkt der Erstellung hat Safari diese Funktion noch nicht bereitgestellt, daher sollten Sie für alles Wesentliche eine Basislinie festlegen.
Zusammenfassung
Die überarbeitete Version von attr() beinhaltet eine kleine Syntaxänderung, die klare Auswirkungen auf die Architektur hat: Werte, die bereits im HTML vorhanden sind, benötigen nicht mehr ein Skript, um zu Styles zu werden. Diese Funktion belohnt Präzision – deklarieren Sie den Typ, geben Sie stets einen Ersatzwert an, lesen Sie das Attribut vom zugehörigen Element ab und übertragen Sie es über benutzerdefinierte Eigenschaften weiter. Akzeptieren Sie außerdem, dass URLs dauerhaft unzugänglich sind. Kombinieren Sie jede wichtige Verwendung mit einer Grundlage, bis Safari verfügbar ist – so kann das Muster bereits heute in die Produktion übernommen werden, wobei die Stylesheet automatisch übernimmt, sobald die Unterstützung ausgeweitet wird. Die formale Definition findet sich im CSS Values and Units Level 5 Draft, falls Sie die genauen Parseregeln benötigen.