Die CSS attr()-Funktion hat nichts gegen benutzerdefinierte Eigenschaften

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Normalerweise besteht die Verbindung zwischen CSS und HTML darin, dass CSS-Selektoren HTML-Elemente auswählen und CSS diese stilisiert. CSS kennt den tatsächlichen Inhalt in HTML nicht. Aber es gibt einen Weg, wie CSS an Daten in HTML gelangen kann, solange diese Daten in einem Attribut dieses HTML-Elements enthalten sind.

Es ist so

div::after {
  content: attr(data-whatever);
}

Das ist sicherlich interessant. Man könnte es zum Beispiel für (eher unzugängliche) Tooltips verwenden.

<button data-tooltip="Information only mouse-having sighted people will see.">
  Button
</button>
button:hover::after {
  content: attr(data-tooltip);
  /* positioned and styled and whatnot */
  /* ya, a :focus style would buy you a tad more a11y */
}

Aber man kann keinen HTML-Code in den Attributwert einfügen, daher sind diese Tooltips auf einen String-Wert beschränkt und könnten keinen Titel, Link oder ähnliches enthalten.

Hier ist ein besserer Anwendungsfall. Es gibt eine alte Marotte für Print-Stylesheets, bei der man attr() verwendet, um die URLs zu Links hinzuzufügen, damit man tatsächlich sehen kann, wohin ein Link führt.

@media (print) {
  a[href]::after {
    content: " (" attr(href) " )";
  }
}

Das ist clever. Aber was noch? Könnte man eine Farbe weitergeben?

<h2 data-color="#f06d06">
  Custom Colored Header
</h2>

Das ist nicht ungültig, aber es ist nicht nützlich.

h2 {
  /* Not gonna work */
  color: attr(data-color);
}

Der Wert von attr() ist ein String. Auch wenn dieser String im gleichen Format wie ein Hex-Code vorliegt, wird er nicht als Hex-Code verwendet.

Man kann auch keine URL übergeben, die tatsächlich in etwas wie background-image() verwendet werden kann. Man kann auch keine Einheit wie 3, 20px oder 4rem oder 0.8vw übergeben.

Die attr()-Funktion von CSS ist nur für Strings, und Strings sind nur wirklich nützlich für content, und content (da es nicht auswählbar und etwas unzugänglich ist) ist sowieso nicht besonders nützlich. Man kann zum Beispiel den Text von Pseudoinhalten nicht auswählen oder danach suchen, was ihn ziemlich unzugänglich macht.

Weißt du, was jeden beliebigen Wert übergeben kann und genauso einfach zu implementieren ist wie Attribute?

CSS-benutzerdefinierte Eigenschaften!

Man kann sie direkt in das style-Attribut jedes Elements einfügen. Jetzt sind diese Werte für dieses Element verfügbar.

<button 
  style="
    --tooltip-string: 'Ug. Tooltips.';
    --tooltip-color: #f06d06;
    --tooltip-font-size: 11px;
    --tooltip-top: -10px
  "
>
  Button
</button>

Oben übergeben wir einen String an CSS, aber auch Farb- und Längenwerte. Diese Werte sind sofort einsatzbereit.

button::after {
  content: var(--tooltip-string);
  color: var(--tooltip-color);
  font-size: var(--tooltip-font-size);
}

Hier ist diese Demo mit etwas fummeliger "Logik" (müsste stark verbessert werden, um wirklich nützlich zu sein), um Variationen zu ermöglichen.

Siehe den Stift CSS Custom Properies Mo’ Betta’ than attr() von Chris Coyier (@chriscoyier) auf CodePen.

Das ist im Übrigen auch nicht zugänglicher. Wenn ich Tooltips wirklich implementieren würde, würde ich dies hier gründlich lesen.

Was ist mit anderen "guten" Anwendungsfällen für attr()?

Einer, der oft vorkommt, sind responsive Datentabellen. Stellen Sie sich eine Tabelle mit Kopfzeilen in der obersten Zeile und Datenzeilen darunter vor.

<table>
  <thead>
  <tr>
    <th>First Name</th>
    <th>Last Name</th>
    ....
  </tr>
  </thead>
  <tbody>
  <tr>
    <td>Chris</td>
    <td>Coyier</td>
    ...
  </tr>
  ...
  </tbody>
</table>

Datenzeilen wie diese können auf kleinen Bildschirmen problematisch werden (zu breit). Daher könnten wir in einer responsiven Datentabelle diese oberste Zeile ausblenden und stattdessen Labels zellenbasiert anzeigen.

@media (max-width: 500px) {
  thead {
    display: none;
  }
  /* Need to reveal another label now that we've hidden the normal labels */
}

Woher kommt dieses Label? Wir könnten es so machen…

 . ...
  <tr>
    <td data-label="First Name">Chris</td>
    <td data-label="Last Name">Coyier</td>
    ...
  </tr>

Dann

td::before { 
  content: attr(data-label);
  /* Also display: block things and such */ 
}

Das ist ein ziemlich guter Anwendungsfall. Wenn wir eine Art zugängliche Verbergungsmethode für dieses <thead> verwenden, könnte es sogar die Zugänglichkeitsprüfung bestehen.

Aber genau dasselbe ist auch mit CSS-benutzerdefinierten Eigenschaften machbar…

 . ...
  <tr>
    <td style="--label: 'First Name';">Chris</td>
    <td style="--label: 'Last Name';">Chris</td>
    ...
  </tr>
td::before { 
  content: var(--label);
  ...
}

Eric Bidelman verwies mich auf eine Methode, Pseudoinhalt zu verwenden, um den Wert eines Eingabefeldes anzuzeigen.

<style>
  input {
    vertical-align: middle;
    margin: 2em;
    font-size: 14px;
    height: 20px;
  }
  input::after {
    content: attr(data-value) '/' attr(max);
    position: relative;
    left: 135px;
    top: -20px;
  }
</style>

<input type="range" min="0" max="100" value="25">

<script>
  var input = document.querySelector('input');

  input.dataset.value = input.value; // Set an initial value.

  input.addEventListener('change', function(e) {
    this.dataset.value = this.value;
  });
</script>

Das fühlt sich für mich etwas gefährlich an, da ich nicht dachte, dass Pseudoinhalt bei ersetzten Elementen wie einem <input> funktionieren würde. Es ist wahrscheinlich eine Aufgabe für output, und die JavaScript-Logik wäre im Wesentlichen dieselbe. Man könnte Pseudoinhalt mit dem zusätzlichen Element verwenden, aber das ist wirklich nicht nötig.


Die Tatsache auszunutzen, dass Pseudoinhalt nicht kopiert werden kann, ist ebenfalls clever. Zum Beispiel macht GitHub Zeilennummern für Codeblöcke mit data-line-number="" und ::before { content: attr(data-line-number); }.

Niemand mag es, Zeilennummern auszuwählen, wenn er versucht, Code zu kopieren! Guter Anwendungsfall hier (wahrscheinlich sogar flexibler als CSS-Zähler), aber auch hier etwas, das CSS-benutzerdefinierte Eigenschaften ebenfalls handhaben könnten.

<td style="--line-num: 5"> ... </td>

Man könnte argumentieren, dass dies besser ist, denn wenn man CSS-Zähler verwenden möchte, könnte man diesen ersten Wert verwenden, um den Startpunkt festzulegen und ihn nicht auf jeder Zeile zu benötigen.

Siehe den Stift Line Numbering von Chris Coyier (@chriscoyier) auf CodePen.

Das Gleiche gilt für typografische Tricks, bei denen Text aus stilistischen Gründen in CSS dupliziert wird. Sehen Sie sich diese coole Demo von Mandy Michael mit attr() an. Ich bin sicher, Sie können sich vorstellen, wie --heading: "Fracture"; die Sache dort erledigen könnte.

Die CSS3 Values-Spezifikation (in Candidate Recommendation) bietet eine Möglichkeit, attr() nützlich zu machen.

Ich bin mir nicht sicher, ob das viel ausmacht, da ich argumentieren würde, dass CSS-benutzerdefinierte Eigenschaften ein nahezu vollständiger Ersatz für attr() sind, aber die Spezifikation behandelt dies ausdrücklich, vermutlich als Versuch, sie nützlicher zu machen.

Die Idee ist, den Typ des Wertes festzulegen, wenn man ihn in CSS abruft.

<div data-color="red">Some Words</div>
div {
  color: attr(data-color color);
}

Oder…

<span data-size="50">span</span>
span {
  font-size: attr(data-size px);
}

Aber soweit ich weiß, unterstützt kein Browser dies.