Woher CSS-Werte kommen, wenn Sie „None“ festlegen: Wie die Vererbung funktioniert
Erfahren Sie, wie Browser entscheiden, ob eine Eigenschaft vererbt wird, warum Kinder den berechneten Wert des Elternteils erhalten, und wie inherit und initial Ihnen eine explizite Kontrolle bieten.
Quellen: How Inheritance Works in CSS Internally at Algorithmic Level.
Man legt color auf ein Wrapper-Element fest, und der darin befindliche Absatz wird rot – obwohl keine Regel dies ausdrücklich vorsieht. Gleichzeitig bleibt der Abstand, den man diesemselben Wrapper-Element zugewiesen hat, genau dort, wo er ist. Beide Verhaltensweisen resultieren aus der CSS-Erbschaft, dem Mechanismus, der einen Wert übernimmt, wenn ein Element keinen eigenen hat. Diese Anleitung erklärt, welche Eigenschaften erbt werden, was genau weitergegeben wird (die Antwort überrascht viele Menschen, wenn Prozentwerte im Spiel sind), wie die Schlüsselwörter inherit und initial die Standardwerte verändern, sowie wo die Erbschaft in den anderen Schritten steht, durch die eine Stylesheet-Datei zu endgültigen Werten wird.
Ein Wert, der von niemandem deklariert wurde
Man beginnt mit einer Regel für ein Elternelement:
.parent {
color: red;
}
und der Markup-Struktur, in der ein Absatz innerhalb dieses Elternelements steht:
<div class="parent">
<p>Hello World</p>
</div>
Es gibt keine Regelsätze, die die color-Eigenschaft im <p>-Element festlegen, dennoch wird der Text rot dargestellt. Zum Zeitpunkt, an dem der Browser diesen Punkt erreicht, hat er bereits konkurrierende Deklarationen durch die Kaskadierung ausgewertet und deren Werte verarbeitet. Was übrig bleibt, ist die Entscheidung dafür, ob für jede Eigenschaft jedes Elements der Wert aus den eigenen Deklarationen des Elements stammen soll oder von einer höher gelegenen Stelle im Baum. Die Vererbung ist das Mechanismus, der diese Entscheidung trifft.
Welche Werte wandern den Baum hinab
Durch die Vererbung können bestimmte Eigenschaftswerte von einem Elternelement auf seine Kinder übertragen werden und von dort weiter auf deren Kinder:
Parent
↓
Child
↓
Grandchild
Nicht jede Eigenschaft nimmt daran teil. Eigenschaften, die den Text beschreiben, sind die klassischen vererbten Eigenschaften, wie zum Beispiel:
font-family
font-size
color
Deshalb muss man die Typografie selten auf jedem Element wiederholen. Anstatt dieselbe Farbe für den body und anschließend wieder für jede Art von Kindelement festzulegen:
body {
color: #333;
}
h1 {
color: #333;
}
p {
color: #333;
}
li {
color: #333;
}
kann man sie einmal, hoch im Dokumentbaum, deklarieren und die Vererbung dafür sorgen, dass sie weitergegeben wird. Das Ergebnis sind weniger CSS-Einträge sowie ein einziger Ort, an dem Änderungen vorgenommen werden können, wenn sich das Design ändert. Die Festlegung der Grundschrift und -farbe auf body oder :root ist die alltägliche Anwendung dieses Konzepts.
Die Entscheidung des Browsers für jede Eigenschaft
Die Logik wird klarer, wenn man sich den Fall ansieht, in dem keine Deklaration für ein Element gilt. Für jede Eigenschaft stellt der Browser im Grunde nacheinander zwei Fragen.
Zuerst: Gibt es einen kaskadierten Wert? Wenn eine Deklaration für diese Eigenschaft im Kaskadensystem dieses Elements gewonnen hat, wird dieser Wert verwendet und die Vererbung spielt keine Rolle.
Zweitens, nur wenn die Antwort nein lautet: Wird diese Eigenschaft vererbt? Das hängt vollständig von der Eigenschaft ab, denn jede wird in den Spezifikationen entweder als vererbt oder nicht vererbt definiert. Einige Beispiele für vererbte Eigenschaften:
color → inherited
font-family → inherited
font-size → inherited
und einige, die nicht vererbt werden:
margin → not inherited
padding → not inherited
Die Trennung ist beabsichtigt. Stellen Sie sich eine Welt vor, in der margin vererbt wird: Ein einziger Rand einer Abschnitt würde auf jeden darin eingebetteten Absatz, jede Liste und jeden Link übertragen werden, wodurch der Abstand auf jeder Ebene zunehmen würde. Eigenschaften, die sich auf Boxen beziehen – wie Margin, Padding, Border und Breite – sind fast immer Dinge, die man pro Element festlegen möchte, während textbezogene Eigenschaften in der Regel so durch eine gesamte Region fließen sollen. Wenn eine Eigenschaft nicht vererbt wird und keinen kaskadierten Wert hat, erhält sie ihren anfänglichen Wert, also den Standardwert, der für diese Eigenschaft definiert ist.
Falls Sie sich bezüglich einer bestimmten Eigenschaft unsicher sind, beantwortet der Eintrag „Vererbt“ in ihrer Referenzdokumentation die Frage direkt.
Kinder erben den berechneten Wert, nicht den von Ihnen eingegebenen Text
Das ist der wichtigste Aspekt in dem gesamten Prozess und zugleich die Ursache für eine bekannte Überraschung. Angenommen, ein Elternelement legt eine Schriftgröße sowie einen Prozentsatz für die Zeilenhöhe fest:
.parent {
font-size: 20px;
line-height: 150%;
}
und ein Kinderelement überschreibt nur die Schriftgröße:
.child {
font-size: 25px;
}
Welche Zeilenhöhe hat das Kinderelement am Ende? Eine intuitive Vermutung wäre, dass das Kinderelement den Prozentsatz selbst erhält:
150%
und diesen anschließend auf seine eigene, größere Schriftgröße anpasst:
150% of 25px
So funktioniert es jedoch nicht. Ein Element erbt den berechneten Wert seines Elternelements, nicht den ursprünglich deklarierten Wert. Für line-height wird ein Prozentsatz zum Zeitpunkt der Berechnung in eine Länge umgewandelt. Beim Elternelement erfolgt die Berechnung wie folgt:
150% of 20px = 30px
somit ist die berechnete Zeilenhöhe des Elternelements:
30px
Genau diese absolute Länge wird weitergegeben. Das Kind erbt trotz seiner größeren Schriftart eine Zeilenhöhe von:
30px
und nicht:
150% of 25px
Die praktische Folge ist, dass die Zeilen immer eng wirken, wenn ein Nachkomme eine größere Schriftart verwendet als das Element, in dem der Prozentsatz definiert wurde. Dasselbe gilt für em-Werte bei line-height, da diese ebenfalls vor der Vererbung in Längen umgewandelt werden. Genau deshalb sind zeilenhöhenlose Werte wie line-height: 1.5 die übliche Empfehlung: Eine einfache Zahl ist bereits der berechnete Wert, sodass jedes Kind sie mit seiner eigenen Schriftgröße multipliziert anstelle einer festen Länge zu erben.
Eigenschaften, die nicht vererbt werden, kehren zu ihrem ursprünglichen Wert zurück
Nehmen wir nun eine Eigenschaft aus der anderen Gruppe:
padding
Padding wird nicht vererbt. Wenn also ein Elternelement folgendes deklariert:
.parent {
padding: 20px;
}
erhält das Kind dies nicht automatisch:
padding: 20px;
Anstelle dessen verwendet das Padding des Kindes aufgrund fehlender kaskadierter Werte und keiner Vererbung den ursprünglichen Wert der Eigenschaft, der für Padding folgendermaßen ist:
0px
Das bleibt so, es sei denn, eine andere Regel gibt dem Kind ausdrücklich ein anderes Padding. Zu wissen, in welche Kategorie eine Eigenschaft fällt, spart Zeit beim Debuggen: Wenn ein Wert scheinbar zu den Kinderelementen „durchsickert“, handelt es sich um eine vererbte Eigenschaft; wenn er bei den Kindern „verschwindet“, handelt es sich um eine nicht vererbte Eigenschaft. Die Entwicklertools des Browsers helfen dabei, da das Computed-Fenster die vererbten Werte zusammen mit ihrem Ursprungselement anzeigt.
Vererbung mit inherit erzwingen
Man kann das Standardverhalten jeder Eigenschaft überschreiben. Das Schlüsselwort inherit weist den Browser an, auch für Eigenschaften, die normalerweise nicht vererbt werden, den Wert des Elternelements zu verwenden:
.child {
padding: inherit;
}
Durch diese Regel entspricht die Füllung des Kindelements der berechneten Füllung des Elternelements. Dies ist nützlich, wenn man eine klare Beziehung zwischen Elternelement und Kindelement haben möchte – beispielsweise damit ein verschachteltes Element die Randfarbe oder den Abstand seines Containers wiederverwenden kann, oder um Formularelemente, die in Browser-Stilen normalerweise keine Schriftarten erben, mit font: inherit zur Verwendung der umgebenden Typografie zu zwingen.
Dies bietet Ihnen zwei unterschiedliche Wege, auf den Wert eines Elternelements zuzugreifen. Automatische Vererbung gilt nur für vererbbare Eigenschaften und ausschließlich dann, wenn kein kaskadierter Wert vorliegt. Explizite Vererbung mit inherit ist an sich eine Deklaration, wodurch sie wie jeder andere Wert in der Kaskade Vorrang hat und für jede Eigenschaft funktioniert.
Aussetzen auf den Initialwert
Das zugehörige Schlüsselwort ist:
initial
Wenn es auf eine Eigenschaft angewendet wird, setzt dies diese Eigenschaft auf ihren vom Standard definierten Initialwert zurück:
.child {
color: initial;
}
Achten Sie darauf, dass „initial“ die Standardwertangabe der Spezifikation bedeutet und nicht den Standard-Stilblatt des Browsers oder den Wert, den ein Element sonst geerbt hätte. Für color ist der Initialwert die Systemtextfarbe (in der Regel schwarz); daher stellt color: initial die Farbe des Elternelements nicht wieder her, sondern trennt das Element davon ab. Für display ist der Initialwert inline; deshalb macht display: initial bei einem div daraus ein Inlinenelement anstelle eines Blockelements.
Nebeneinander erfüllen die beiden Schlüsselwörter gegensätzliche Funktionen:
inherit → use the parent's value
initial → reset to the property's initial value
Zusammen geben sie Ihnen eine explizite Kontrolle darüber, wie Werte durch den Baum übertragen werden. CSS bietet außerdem unset (das sich für vererbte Eigenschaften wie inherit und für alle anderen wie initial verhält) sowie revert (das auf die Standard-Sheet-Style des Browsers zurückgreift); beides ist oft nützlicher als initial, um Komponenten zurückzusetzen.
Das Gesamtbild zusammenfassen
Es hilft, die Vererbung im Zusammenhang mit den vorhergehenden Schritten zu betrachten. Wenn mehrere Regeln auf dasselbe Element zutreffen, beginnt der Browser mit der Kaskade, bei der die Deklarationen in folgender Reihenfolge verglichen werden:
Importance
↓
Specificity
↓
Source Order
Der Gewinner ist der kaskadierte Wert. Der Wert durchläuft anschließend eine Reihe von Verarbeitungsstufen, bis er zu etwas wird, das für Layout und Zeichnen verwendet werden kann:
Declared Value
↓
Cascaded Value
↓
Specified Value
↓
Computed Value
↓
Used Value
↓
Actual Value
Wenn kein kaskadierter Wert vorhanden ist, liefert die Vererbung stattdessen den Eingang für diese Sequenz:
No Cascaded Value
↓
Can Property Be Inherited?
↓
Yes
↓
Parent's Computed Value
↓
Child
Genauer gesagt legt die Spezifikation die Vererbung an den Punkt, an dem der spezifizierte Wert bestimmt wird: Der spezifizierte Wert ist der kaskadierte Wert, sofern dieser vorhanden ist; ansonsten der berechnete Wert des Elternteils für die vererbbaren Eigenschaften; andernfalls der Anfangswert. Von dort aus setzen sich die üblichen Phasen „berechnet“, „verwendet“ und „tatsächlich“ fort. Ein vereinfachtes Gesamtbild der CSS-Verarbeitungsphase sieht daher so aus:
CSS
↓
Cascade
↓
┌─────────────┴─────────────┐
↓ ↓
Importance Specificity
↓
Source Order
↓
Cascaded Value
↓
Value Processing
↓
Computed / Used / Actual
↓
Inheritance
↓
CSSOM
Die vertikale Anordnung in diesem Diagramm sollte als Gruppierung von Konzepten und nicht als strikte Zeitlinie verstanden werden, da die Vererbung den angegebenen Wert bereitstellt anstatt nach bereits berechneten Werten zu handeln. Die Kernidee bleibt unverändert: Zwischen Stylesheet und Bildschirm geschehen weitaus mehr Dinge als nur „Ich habe eine Regel geschrieben und der Browser hat sie angewendet“.
Von Stylesheet zu Pixeln
Wenn man den Überblick behält, zeigt sich hier die vollständige Abfolge mit erweitertem CSS-Teil:
HTML
↓
DOM
↓
CSS
↓
Cascade
↓
Specificity
↓
Value Processing
↓
Inheritance
↓
CSSOM
↓
DOM + CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Pixels
Angesichts dieses Ablaufs beinhaltet selbst eine kleine Regel viele implizierte Aufgaben:
.title {
font-size: 2rem;
color: blue;
}
Bevor etwas dargestellt wird, muss der Browser zunächst feststellen:
- welche Deklarationen auf jedes Element zutreffen;
- welche davon im Falle eines Konflikts Vorrang hat;
- wie spezifisch die konkurrierenden Selektoren sind;
2rem in Pixeln ist);Nur nach all dem geht die Seite zur Layout- und Darstellungsebene über.
CSS als System statt als Liste von Regeln
Viel der Verwirrung um CSS verschwindet, sobald man aufhört, es als isolierte Regeln zu betrachten und anfängt, es als ein System mit klaren Aufgabenbereichen zu sehen:
- Spezifität erklärt, warum eine Deklaration einer anderen vorgezogen wird.
- Quellreihenfolge entscheidet bei gleicher Spezifität.
- Wertverarbeitung wandelt das, was man geschrieben hat, in Werte um, die der Browser verwenden kann.
- Vererbung erklärt, woher Werte stammen, wenn Sie keine festgelegt haben.
Zusammen verwandeln sie eine Stylesheet-Datei in einen vollständigen Satz endgültiger Werte, die für das Layout und die Darstellung verwendet werden. Selbst eine so kurze Deklaration wie diese:
width: 66%;
wird durch alle Schritte geleitet. Der Browser bestimmt, welche Regel anwendbar ist, verarbeitet den Prozentsatz, bezieht ihn auf den enthaltenden Block und legt schließlich eine endgültige Breite fest – all das, bevor auch nur ein Pixel angezeigt wird.
Was danach bleibt, ist eine separate Frage: Wo genau soll jedes Element platziert werden? Breiten, Höhen, der normale Fluss, die Positionierung sowie das Box-Modell gehören zum visuellen Formatierungsmodell, der nächsten Stufe im Prozess, das diese endgültigen Werte in ein Layout umwandelt.
Kernpunkte
- Eine Eigenschaft erhält ihren Wert zunächst aus der Kaskade; die Vererbung wird nur herangezogen, wenn keine Deklaration vorliegt.
- Jede Eigenschaft wird als vererbt (meist textbezogen:
color,font-*) oder nicht vererbt (meist kastenbezogen:margin,padding) definiert. - Kinder erben den berechneten Wert des Elternteils, sodass Prozentsatz- oder
em-Werte für die Zeilenhöhe als feste Längen weitergegeben werden; es ist besser, einen einheitlosenline-heightzu verwenden. - Nicht vererbte Eigenschaften ohne Angabe verwenden ihren initialen Wert, wie beispielsweise
0für das Padding. inheritzwingt zur Vererbung jeder Eigenschaft;initialsetzt auf den Standardwert der Spezifikation, nicht auf den Wert des Elternteils;unsetundrevertsind oft bessere Tools zur Zurücksetzung.