Erkundung der Komplexität von Breite und Höhe in CSS

Avatar of Uri Shaked
Uri Shaked am

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

Der folgende Artikel wurde gemeinsam von Uri Shaked und Michal Porag verfasst.

Lassen Sie uns die Komplexität untersuchen, wie CSS die Breiten- und Höhenabmessungen von Elementen berechnet. Dies basiert auf unzähligen späten Nachtstunden beim Debugging und Herumspielen mit vielen Kombinationen von CSS-Eigenschaften, beim Lesen der Spezifikationen und beim Versuch herauszufinden, warum einige Dinge auf die eine oder andere Weise zu funktionieren scheinen.

Aber bevor wir eintauchen, lass uns einige Grundlagen behandeln, damit wir alle auf dem gleichen Stand sind.

Die Grundlagen

Sie haben ein Element und möchten, dass es 640 Pixel breit und 360 Pixel hoch ist. Dies sind nur willkürliche Zahlen, die dem Pixelverhältnis von 16:9 entsprechen. Sie können sie explizit wie folgt festlegen:

.element {
  width: 640px;
  height: 360px;
}

Nun erfordert das Design etwas padding innerhalb dieses Elements. Also ändern Sie das CSS

.element {
  width: 640px;
  height: 360px;
  padding: 10px;
}

Was ist die gerenderte Breite und Höhe des Elements jetzt? Ich wette, Sie können es erraten... es ist nicht mehr 640x360px! Es ist tatsächlich 660x380px, da der Innenabstand 10 Pixel auf jeder Seite hinzufügt (d. h. oben, rechts, unten und links), was zusätzlich 20 Pixel sowohl zur Höhe als auch zur Breite ergibt.

Dies hat mit der Eigenschaft box-sizing zu tun: Wenn sie auf content-box (der Standardwert) gesetzt ist, ist die gerenderte Größe des Elements die Breite und Höhe *plus* das padding und der border. Das kann bedeuten, dass die gerenderte Größe größer ist als beabsichtigt, was lustig ist, weil die deklarierten width- und height-Werte eines Elements völlig anders sein können als das, was gerendert wird.

Das ist die Kraft von The CSS Box Model. Es berechnet Breite und Höhe wie folgt:

/* Width */
width + padding-left + padding-right + border-left + border-right

/* Height */
height + padding-top + padding-bottom + border-top + border-bottom

Was wir gerade gesehen haben, ist, wie die Abmessungen für ein **Blockelement** berechnet werden. Blockelemente umfassen jedes Element, das von Natur aus die volle verfügbare Breite einnimmt. Daher spielt es keine Rolle, wie viel Inhalt das Element enthält, da seine Breite immer 100 % beträgt, es sei denn, wir ändern sie. Denken Sie an Elemente wie <p>, <article>, <main>, <div> und noch viele mehr.

Aber jetzt sollten wir uns Inline-Elemente ansehen, da sie sich anders verhalten, wenn es um The Box Model und die Berechnung ihrer Abmessungen geht. Danach werden wir uns die Beziehung zwischen Eltern- und Kindelementen ansehen und wie sie die Breiten- und Höhenberechnungen voneinander beeinflussen.

Der kuriose Fall von Inline-Elementen

Wie wir gerade gesehen haben, sind das padding und der border jedes Elements sowohl in den berechneten Breiten- und Höhenabmessungen des Elements enthalten. Lustigerweise gibt es Fälle, in denen die Eigenschaften width und height überhaupt keine Wirkung haben. Das ist der Fall, wenn man mit **Inline-Elementen** arbeitet.

Ein Inline-Element ist ein Element, dessen Breite und Höhe durch den enthaltenen Inhalt bestimmt werden. Inline-Elemente wie ein <span> ignorieren die Eigenschaften width und height sowie die Eigenschaften für den oberen und unteren margin vollständig, da der Inhalt die Abmessungen bestimmt. Hier kann manchmal eine visuelle Darstellung helfen.

Schauen Sie sich einfach an, wie das Verschachteln eines Blockelements innerhalb eines Inlineelements die Form des Inlineelements zerstört, nur weil das Blockelement nicht durch die Menge des enthaltenen Inhalts definiert wird, sondern durch *die Menge des verfügbaren Platzes*. Das sehen Sie wirklich in Aktion, wenn wir dem Inlineelement einen Rahmen hinzufügen. Sehen Sie, wie das Inlineelement dort abrupt aufhört, wo der Absatz beginnt, und dann nach dem Absatz weitergeht.

Der Span sieht den Absatz, der den Inline-Fluss des Spans unterbricht und ihn im Wesentlichen daraus ausbricht. Faszinierende Sache!

Aber es gibt noch mehr! Sehen Sie, wie das Inlineelement width und margin vollständig ignoriert, auch wenn diese darauf deklariert sind.

Verrückt!

Eltern- und Kindelemente

Die Eltern-Kind-Beziehung ist ein gängiges Muster. Ein Elternelement ist eines, das andere darin verschachtelte Elemente enthält. Und diese verschachtelten Elemente sind die Kinder des Elternelements.

<!-- The parent element -->
<div class="parent">
  <!-- The children -->
  <div class="child"></div>
  <div class="another-child"></div>
  <div class="twins">Whoa!</div>
</div>

Die Breite und Höhe eines Elements werden mit Eltern- und Kindelementen besonders interessant. Betrachten wir all die interessanten Eigenheiten, die wir hierbei erhalten.

Relative Einheiten

Beginnen wir mit relativen Einheiten. Eine relative Einheit wird durch ihren Kontext oder ihre Beziehung zu anderen Elementen berechnet. Ja, das ist eine etwas komplizierte Definition. Es gibt verschiedene Arten von relativen Einheiten, aber nehmen wir Prozente als Beispiel. Wir können sagen, dass ein Element 100 % breit ist.

<div class="child">
  <!-- nothing yet -->
</div>
.parent {
  width: 100%;
}

Kühl. Wenn wir dieses Element auf eine leere Seite legen, nimmt es 100 % des verfügbaren horizontalen Platzes ein. Und wozu diese 100 % berechnet werden, hängt von der Breite des Browsers ab, richtig? 100 % eines Browsers, der 1.500 Pixel breit ist, sind 1.500 Pixel breit. 100 % eines Browsers, der 500 Pixel breit ist, sind 500 Pixel breit. Das meinen wir mit einer *relativen Einheit*. Der tatsächliche berechnete Wert wird durch den Kontext bestimmt, in dem er verwendet wird.

Der aufmerksame Leser denkt also vielleicht schon: "Hey, das ist so etwas wie ein Kindelement, das auf die Breite des Elternelements eingestellt ist." Und das wäre richtig. Die Breite des Kindes zu 100 % wird basierend auf der tatsächlichen Breite des Elternelements berechnet, das es enthält.

Die Höhe funktioniert im Grunde genauso: Sie ist relativ zur Höhe des Elternteils. Zum Beispiel führen zwei Elternelemente mit unterschiedlichen Höhenabmessungen, aber identischen Kindern zu Kindern mit unterschiedlichen Höhen.

Padding und Margin

Die Breite und Höhe von Eltern-Kind-Kombinationen werden noch interessanter, wenn wir andere Eigenschaften wie padding und margin betrachten. Anscheinend ist, wenn wir einen Prozentsatz für Padding oder Margin angeben, dieser immer relativ zur *Breite* des Elternelements, selbst wenn es um vertikale Kanten geht.

Einige clevere Designer haben dies genutzt, um Boxen mit gleicher Breite und Höhe zu erstellen oder um Boxen, die beim Ändern der Seitengröße ein bestimmtes Seitenverhältnis beibehalten. Dies ist besonders nützlich für Video- oder Bildinhalte, kann aber auch kreativ (und missbräuchlich) eingesetzt werden. Tippen Sie einfach ein, was immer Sie wollen, in das editierbare Element dieser Demo. Die Box behält eine proportionale Höhe und Breite bei, unabhängig davon, wie viel (oder wenig) Inhalt hinzugefügt wird.

Diese Technik zum Erstellen von Seitenverhältnis-Boxen wird liebevoll als "Padding Hack" bezeichnet. Chris hat sie ausführlich behandelt. Aber jetzt, da die Eigenschaft aspect-ratio breite Browserunterstützung findet, gibt es weniger Grund, darauf zurückzugreifen.

display: inline und inline-block

Nachdem wir uns angesehen haben, wie die Abmessungen von Eltern- und Kindelementen berechnet werden, sollten wir uns zwei weitere interessante Eigenschaftswerte ansehen, die die Breite eines Elements beeinflussen: min-content und max-content.

Diese Eigenschaften weisen den Browser an, den Inhalt des Elements zu betrachten, um seine Breite zu bestimmen. Wenn wir zum Beispiel den Text haben: "Hallo CSS-Enzyklopädie, nett, Sie kennenzulernen!", würde der Browser den Platz berechnen, den dieser Text auf dem Bildschirm einnimmt, und ihn als Breite verwenden.

Der Unterschied zwischen min-content und max-content liegt darin, wie der Browser diese Berechnung durchführt. Für max-content tut der Browser so, als hätte er unendlich viel Platz und legt den gesamten Text in einer einzigen Zeile an und misst seine Breite.

Für min-content tut der Browser so, als hätte er null Platz, also legt er jedes Wort / Inline-Kindelement in eine eigene Zeile. Sehen wir uns das in Aktion an.

Wir haben max-content bereits in Aktion gesehen, als wir uns den Unterschied zwischen Block- und Inline-Elementen angesehen haben. Inline-Elemente sind, erinnern Sie sich, nur so breit und hoch wie der Inhalt, den sie enthalten. Wir können die meisten Elemente zu Inline-Elementen machen, indem wir einfach display: inline; darauf deklarieren.

Kühl. Eine weitere Waffe, die wir haben, ist display: inline-block;. Das erzeugt ein Inline-Element, aber angereichert mit Block-Level-Berechnungen im Box Model. Mit anderen Worten, es ist ein Inline-Element, das margin, width und height respektiert. Das Beste aus beiden Welten!

Zyklische Prozentgrößenberechnung

Hatte der letzte Punkt Sinn? Nun, hoffentlich verwirre ich Sie nicht mit diesem:

Das Kindelement in diesem Beispiel hat eine relative Breite von 33 %. Das Elternelement hat keine Breite deklariert. Wie zum Teufel wird die berechnete Breite des Kindes berechnet, wenn nichts Relatives dazu vorhanden ist?

Um das zu beantworten, müssen wir uns ansehen, wie der Browser die Größe der Elemente in diesem Beispiel berechnet. Wir haben keine spezifische Breite für das Elternelement definiert, daher verwendet der Browser den Anfangswert für width, der auto ist. Und da das display des Elternelements auf inline-block gesetzt ist, verhält sich auto wie max-content. Und max-content, wie wir gesehen haben, bedeutet, dass das Elternelement so breit ist wie der Inhalt darin, nämlich alles im Kindelement.

Daher betrachtet der Browser den Inhalt des Elements (Kinder), um seine Breite zu bestimmen. Die Breite des Kindes hängt jedoch auch von der Breite des Elternelements ab! Verdammt, das ist seltsam!

Die CSS Box Sizing Module-Spezifikation nennt dies zyklische Prozentgrößenberechnung. Ich bin mir nicht sicher, warum sie genau so genannt wird, aber sie beschreibt die komplexe Mathematik, die der Browser durchführen muss, um (1) die Breite des Elternelements zu bestimmen und (2) diese Breite mit der relativen Breite des Kindes zu vereinbaren.

Der Prozess ist eigentlich ziemlich cool, sobald man sich mit der Mathematik auseinandergesetzt hat. Der Browser beginnt damit, eine temporäre Breite für das Kind zu berechnen, *bevor* sein deklarierter Wert angewendet wird. Die temporäre Breite, die der Browser für das Kind verwendet, ist auto, was, wie wir gesehen haben, wie max-content wirkt, was wiederum dem Browser sagt, dass das Kind so breit sein muss wie der Inhalt, den es enthält. Und im Moment ist das nicht der deklarierte Wert von 33 %.

Dieser max-content-Wert ist das, was der Browser zur Berechnung der Breite des Elternelements verwendet. Das Elternelement muss, sehen Sie, mindestens so breit sein wie der Inhalt, den es enthält, der alles im Kindelement bei max-content ist. Sobald das gelöst ist, geht der Browser zurück zum Kindelement und wendet den in CSS deklarierten Wert von 33 % an.

So sieht das aus:

Da haben wir's! Jetzt wissen wir, wie ein Kindelement zum berechneten Wert seines Elternelements beitragen kann.

M&Ms: Die min- und max- Eigenschaften

Hey, Sie sind sich wahrscheinlich bewusst, dass die folgenden Eigenschaften existieren:

  • min-width
  • min-height
  • max-width
  • max-height

Nun, diese haben auch viel mit der Breite und Höhe eines Elements zu tun. Sie legen die Grenzen für die Größe eines Elements fest. Es ist, als würde man sagen: "Hey, Browser, stell sicher, dass dieses Element nie unter dieser Breite/Höhe oder über dieser Breite/Höhe liegt."

Selbst wenn wir eine explizite Breite für ein Element deklariert haben, sagen wir 100 %, können wir diesen Wert immer noch begrenzen, indem wir ihm eine max-width geben.

element {
  width: 100%;
  max-width: 800px;
}

Dies ermöglicht es dem Browser, dem Element so viel Platz zu geben, wie es möchte, bis zu 800 Pixel. Sehen wir uns an, was passiert, wenn wir die Werte vertauschen und die max-width auf 100 % und die width auf 800 px setzen.

element {
  width: 800px;
  max-width: 100%;
}

Hey, es scheint das gleiche Verhalten zu ergeben! Das Element nimmt den gesamten benötigten Platz ein, bis es 800 Pixel erreicht.

Anscheinend werden die Dinge komplexer, sobald wir ein Elternelement hinzufügen. Dies ist das gleiche Beispiel wie oben, mit einer wichtigen Änderung: Jetzt ist jedes Element ein Kind eines inline-block-Elements. Plötzlich sehen wir einen deutlichen Unterschied zwischen den beiden Beispielen.

Warum der Unterschied? Um das zu verstehen, betrachten wir, wie der Browser die Breite der Elemente in diesem Beispiel berechnet.

Wir beginnen mit dem Elternelement (.parent). Es hat eine display-Eigenschaft, die auf inline-block gesetzt ist, und wir haben keinen width-Wert dafür angegeben. Genau wie zuvor betrachtet der Browser die Größe seiner Kinder, um seine Breite zu bestimmen. Jede Box in diesem Beispiel ist im .parent-Element verschachtelt.

Die erste Box (#container1) hat einen prozentualen width-Wert von 100 %. Die Breite des Elternelements ergibt sich aus der Breite des darin enthaltenen Textes (max-content des Kindes), begrenzt durch den von uns mit max-width angegebenen Wert, und dieser wird auch zur Berechnung der Breite des Kindes verwendet.

Die zweite Box (#container2) hat eine festgelegte Breite von 800 Pixeln, sodass ihre Elternelementbreite ebenfalls 800 Pixel beträgt – gerade breit genug, um ihr Kind aufzunehmen. Dann wird die max-width des Kindes relativ zur endgültigen Breite des Elternelements aufgelöst, nämlich 800 Pixel. In diesem Fall sind also sowohl das Elternelement als auch das Kind 800 Pixel breit.

Obwohl wir also anfangs gesehen haben, dass die beiden Boxen gleich funktionieren, als wir die Werte für width und max-width vertauschten, wissen wir jetzt, dass das nicht immer der Fall ist. In diesem Fall hat die Einführung eines Elternelements mit display: inline-block; alles durcheinandergebracht!

Hinzufügen von min(), max() und clamp() zur Mischung

min(), max() und clamp() sind drei nützliche CSS-Funktionen, mit denen wir die Größe von Elementen responsiv definieren können... ohne Media Queries!

  • min(): Gibt den kleinsten Wert seiner Argumente zurück. Die Argumente können in verschiedenen Einheiten angegeben werden, und wir können sogar absolute und relative Einheiten mischen und abgleichen, wie min(800px, 100%).
  • max(): Gibt den größten Wert seiner Argumente zurück. Genau wie bei min() können Sie verschiedene Einheiten mischen und abgleichen.
  • clamp(): Eine Kurzschreibweise für die gleichzeitige Durchführung von min und max: clamp(MIN, VAL, MAX) wird als max(MIN, min(VAL, MAX)) aufgelöst. Mit anderen Worten, es gibt VAL zurück, es sei denn, es überschreitet die Grenzen, die durch MIN und MAX definiert sind, in welchem ​​Fall es den entsprechenden Grenzwert zurückgibt.

So. Sehen Sie, wie wir das max-width-Beispiel von oben effektiv mit einer einzigen CSS-Eigenschaft "umschreiben" können.

.element {
  width: min(800px, 100%);
}

/* ...is equivalent to: */
.element {
  width: 800px;
  max-width: 100%;
}

Das würde die Breite des Elements auf 800 Pixel setzen, aber sicherstellen, dass wir nicht die Breite des Elternelements (100 %) überschreiten. Genau wie zuvor, wenn wir das Element mit einem inline-block-Elternelement umschließen, können wir beobachten, wie es sich anders verhält als die max-width-Variante.

Die Breite der Kinder (800 Pixel) ist gleich. Wenn Sie jedoch den Bildschirm vergrößern (oder den 0,5-fachen Button von CodePen verwenden, um herauszuzoomen), werden Sie feststellen, dass das zweite Elternelement tatsächlich größer ist.

Es kommt darauf an, wie der Browser die Breite des Elternelements berechnet: Wir haben keine Breite für das Elternelement angegeben, und da der width-Wert des Kindes relative Einheiten verwendet, ignoriert der Browser ihn bei der Berechnung der Breite des Elternelements und verwendet das max-content-Kind des Kindes, diktiert durch den Text "very long ... long".

Zusammenfassung

Puh! Es ist verrückt, dass etwas so scheinbar Einfaches wie width und height tatsächlich viel zu bieten hat. Sicher, wir können explizite width- und height-Werte für ein Element festlegen, aber die tatsächlichen Werte, die gerendert werden, sind oft etwas ganz anderes.

Das ist die Schönheit (und ehrlich gesagt, der Frust) von CSS. Es ist so sorgfältig durchdacht und berücksichtigt so viele Ausnahmefälle. Ich meine, das Konzept des Box Models selbst ist wunderbar komplex und elegant zugleich. Wo sonst können wir explizit etwas im Code deklarieren und es auf verschiedene Weise interpretieren lassen? Die width ist nicht immer die Breite.

Und wir haben uns noch nicht einmal mit einigen anderen Faktoren befasst, die zu den Abmessungen eines Elements beitragen. Moderne Layouttechniken wie CSS Flexbox und Grid führen Achsen und Tracklinien ein, die ebenfalls die gerenderte Größe eines Elements bestimmen.


Autoren: Uri Shaked und Michal Porag