CSS-Mauerwerk mit Flexbox

Oberflächlich betrachtet scheint es ziemlich einfach zu sein, mitflexbox ein Mauerwerkslayout zu erstellen . alles was Sie brauchen gesetzt tun flex-flowzu column wrapund voilà, Sie ein Mauerwerk Layout. Art von. Das Problem bei diesem Ansatz ist, dass ein Gitter mit einer scheinbar gemischten und undurchsichtigen Reihenfolge erzeugt wird. Die Elemente werden (dem Benutzer nicht bekannt) von oben nach unten gerendert, und jemand, der das Raster von links nach rechts analysiert, liest die Kästchen in einer etwas willkürlichen Reihenfolge, beispielsweise 1, 3, 6, 2, 4, 7, 8, 5 und so weiter und so fort.

Mit Flexbox ist es nicht einfach, Elemente mit einem column Layout zu rendern, während ein row Auftrag verwendet wird. Mit :nth-child() orderEigenschaft können wir jedoch ein Maurerlayout nur mit CSS erstellen, für das kein JavaScript erforderlich ist .Im Kern ist hier der Trick, um eine row Reihenfolge zu erstellen flex-direction: column , wenn Sie drei Spalten rendern:

/* Render items as columns */
.container {
  display: flex;
  flex-flow: column wrap;
}

/* Re-order items into rows */
.item:nth-child(3n+1) { order: 1; }
.item:nth-child(3n+2) { order: 2; }
.item:nth-child(3n)   { order: 3; }

/* Force new columns */
.container::before,
.container::after {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 2;
}

Dadurch wird ein Mauerwerkslayout mit Elementen erstellt, die als Spalten gerendert, aber als Zeilen angeordnet sind (die grauen vertikalen Linien stellen die Pseudoelemente dar, die Zeilenumbrüche erzwingen :

1
2
3
4
5
6
7
8
9
10

Lassen Sie uns das Problem aufschlüsseln (oder zur Lösung springen oder die Codepensammlung ansehen ).

Wählen Sie Ihr Gift: eine gemischte Bestellung oder seltsame Lücken

Flexbox ist nicht wirklich gebaut für Mauerwerk , wenn Sie eine feste Höhe auf einen flexiblen Behälter gesetzt (so dass Gegenstände können einpacken , wenn sie Überlauf) und flex-flowzu column wrap, Sie so etwas wie dies erreichen werden:

1
2
3
4
5
6
7
8
9
10

Die Elemente werden in Spalten von oben nach unten gerendert, sodass beim Lesen von links nach rechts eine beliebige Reihenfolge entsteht. Dies ist natürlich das erwartete und in vielen Szenarien erwünschte Ergebnis, aber nicht, wenn wir versuchen, ein Mauerwerkslayout zu erstellen, und es wird zunehmend desorientiert, wenn eine Seite größer wird.

Wenn wir stattdessen das flex-directionto ändern rowund Elemente unterschiedlicher Höhe haben, erreichen wir die richtige Reihenfolge, aber mit seltsamen und unerwarteten Lücken in unserem gesamten Raster:

1
2
3
4
5
6
7
8
9
10

Es scheint also unmöglich, das Beste aus beiden Welten zu bekommen: Elemente, die als Spalten gerendert, aber als Zeilen geordnet werden. Möglicherweise möchten Sie flex-direction: columndie Elemente in Ihrem HTML- Code verwenden und sich nur um sie bewegen, um die richtige visuelle Reihenfolge zu erreichen. Dies kann jedoch umständlich sein, ist unnötig komplex und führt zu einer Verfälschung der Tabulatorreihenfolge der Elemente.

Elemente mit orderund nth-child()anordnen

Die orderEigenschaft wirkt sich auf die Reihenfolge der Elemente aus, die in einer CSS-Flexbox oder einem CSS-Raster enthalten sind, und kann verwendet werden, um Elemente für unser zukünftiges Mauerwerkslayout neu zu ordnen. Die Verwendung der orderEigenschaft ist recht unkompliziert: Wenn Sie zwei Elemente haben und eines hat order: 1und das andere hat, wird order: 2das Element mit order: 1unabhängig von der HTML-Quellcode-Reihenfolge vor dem anderen Element gerendert.

Die CSS-Mauerwerkslösung hängt von einem Detail der orderSpezifikation ab: Was passiert, wenn zwei oder mehr Elemente denselben orderWert haben? Welches kommt zuerst? Flexbox greift auf die Quellcode-Reihenfolge zurück: Das Element, das zuerst im Quellcode erscheint, wird vor anderen Elementen mit demselben Bestellwert gerendert. Diese Tatsache gibt uns die Möglichkeit, Elemente in unserem Raster einfach neu zu gruppieren, sodass wir die Reihenfolge von Spalten zu Zeilen ändern können, während wir diese Zeilen weiterhin als Spalten rendern nth-child().

Schauen Sie sich die Tabelle unten an. Um eine sinnvolle Reihenfolge zu erreichen, müssten flex-direction: rowwir die Elemente nur in der Standardreihenfolge rendern: 1, 2, 3, 4, 5, 6usw.

  Spalte 1 Spalte 2 Spalte 3
Zeile 1 1 2 3
Reihe 2 4 5 6
Reihe 3 7 8 9
Zeile 4 10 11 12

Wenn wir bei der Verwendung dieselbe Reihenfolge erreichen möchten, müssen flex-direction: columnwir die Reihenfolge der Elemente ändern, um sie an die Reihenfolge der einzelnen Spalten in der Tabelle anzupassen (und nicht an die Reihenfolge der einzelnen Zeilen):

  Spalte 1 Spalte 2 Spalte 3
Zeile 1 1 2 3
Reihe 2 4 5 6
Reihe 3 7 8 9
Zeile 4 10 11 12

Dh die ersten Elemente in unserem Flexbox-Layout müssen sein 1, 4, 7, 10. Diese Elemente füllen die erste Spalte aus, gefolgt von 2, 5, 8, 11der zweiten Spalte und 3, 6, 9, 12der dritten und letzten Spalte. Hier kommt der nth-child()Selektor ins Spiel. Wir können damit jedes dritte Element (3n) auswählen, beginnend mit dem ersten Element (3n + 1), und alle diese Elemente auf denselben orderWert setzen:

.item:nth-child(3n+1) { order: 1; }

Dieser Selektor setzt das order: 1Element 1, 4, 7, 10in unserem Container, dh die gesamte erste Spalte. Mit anderen Worten, wir verwenden eine Kombination von nth-child()und order, um Artikel in Abhängigkeit von ihrer ursprünglichen Bestellung nachzubestellen. Um die 2. und 3. Spalte zu erstellen, ändern wir einfach den Versatz:


.item:nth-child(3n+1) { order: 1; }
.item:nth-child(3n+2) { order: 2; }
.item:nth-child(3n)   { order: 3; }

Hier produzieren wir drei Mengen: 1, 4, 7, 10(3n + 1) mit order: 1, 2, 5, 8, 11(3n + 2) mit order: 2und 3, 6, 9, 12(3n) mit order: 3. Alles in allem wird die Reihenfolge 1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12.

Wenn wir sicherstellen, dass jede dieser Gruppen als eine Spalte (und nicht mehr) dargestellt wird, entsteht die Illusion, dass die Elemente beim Lesen von links nach rechts in ihre ursprüngliche Reihenfolge zurückgekehrt sind. Wenn wir das Raster visuell als Zeilen analysieren, enthält die erste Zeile das erste Element aus jeder Gruppe ( 1, 2, 3), die zweite Zeile das zweite Element aus jeder Gruppe ( 4, 5, 6) usw. Mit dieser Technik können wir ein Mauerwerkslayout mit Elementen erstellen, die als Spalten gerendert, aber als Zeilen angeordnet sind.

1
2
3
4
5
6
7
8
9
10

Wie wirkt sich das Mischen um solche Elemente auf die Tab-Reihenfolge aus? Zum Glück überhaupt nicht. orderÄndert nur die visuelle Darstellung von Objekten, nicht die Aktivierreihenfolge. Das Durchlaufen des Rasters funktioniert also wie vorgesehen.

Verhindern, dass Spalten zusammengeführt werden

Wenn Sie viele Elemente in einem Mauerwerk-Layout haben, wird diese Technik mit ziemlicher Sicherheit irgendwann zusammenbrechen. Wir gehen davon aus, dass jede von uns erstellte „Gruppe“ als genau eine Spalte gerendert wird, aber in Wirklichkeit können Elemente unterschiedliche Höhen haben und Spalten können leicht zusammengeführt werden. Die erste Spalte könnte beispielsweise länger sein als die beiden anderen, wodurch die dritte Spalte am Ende der zweiten Spalte beginnen könnte:

1
2
3
4
5
6
7
8
9
10

Das hervorgehobene Kästchen hier (3) muss am Anfang der dritten Spalte gerendert werden, sonst bricht der Ordnungsalgorithmus zusammen, aber wenn am Ende der zweiten Spalte Platz für ein anderes Element ist, wird es natürlich dort gerendert.

Wir können dieses Umbruchproblem beheben, indem wir Spalten zwingen, an bestimmten Punkten neu zu starten. Es gibt keine einfache Möglichkeit, mit flexbox zu sagen, dass dieses Element einen Zeilenumbruch aufweisen sollte, aber wir können diesen Effekt erzielen, indem wir unsichtbare Elemente hinzufügen, die 100% der Containerhöhe einnehmen . Da sie 100% der Höhe des übergeordneten Elements benötigen, um gerendert zu werden, passen sie nicht zusammen mit anderen Elementen in eine Spalte. Daher erzwingen sie Zeilenumbrüche, indem sie reduzierte Spalten erstellen.

Wir haben diesen Zeilenumbruch Elemente in unser Netz und Anordnung von Elementen eingefügt werden , also was wir suchen ist diese Folge von Elementen zu erstellen: 1, 4, 7, 10, <break>, 2, 5, 8, 11, <break>, 3, 6, 9, 12. Wir können Pseudoelemente für den Container verwenden, um diese hinzuzufügen, und wir können den orderWert 2auf beide setzen. Wenn Sie ein Pseudoelement mit :beforehinzufügen, wird es zum ersten untergeordneten Element :afterdes Containers, und wenn Sie ein Pseudoelement mit hinzufügen, wird es zum letzten untergeordneten Element des Containers. Wenn Sie also order: 2beide festlegen , werden sie zum ersten und letzten Element von die order: 2„Gruppe“ (wie sie vor und nach den anderen Elementen erscheinen) :before, 2, 5, 8, 11, :after.

/* Force new columns */
.container::before,
.container::after {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 2;
}

Ich habe die Pseudoelemente unten hervorgehoben, um ihre Wirkung zu zeigen. Beachten Sie, dass das Kästchen 3 trotzdem in die 2. Spalte passt und als erstes Element in der letzten Spalte dargestellt wird:

1
2
3
4
5
6
7
8
9
10

Die Lösung

Als letzten Schritt müssen Sie sicherstellen, dass Ihr Flex-Container eine festgelegte Höhe hat, die ihn höher als Ihre höchste Spalte macht (damit alle Spalten passen). Zusammen ergibt dies ein CSS-Mauerwerkslayout mit drei Spalten ( auch als Codepen verfügbar ):

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 600px; 
}

.item {
  width: 32%;
  margin-bottom: 2%; /* Optional */
}

/* Re-order items into 3 rows */
.item:nth-child(3n+1) { order: 1; }
.item:nth-child(3n+2) { order: 2; }
.item:nth-child(3n)   { order: 3; }

/* Force new columns */
.container::before,
.container::after {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 2;
}

Ihr HTML sollte so aussehen, mit einem

für jedes Element in Ihrem Raster:

...

Arbeiten mit mehr als drei Spalten

Um ein Mauerwerkslayout mit mehr als drei Spalten zu erstellen, müssen wir einige Dinge tun: unseren Sortieralgorithmus anpassen, die Breite der Elemente aktualisieren und Zeilenumbruchelemente manuell einfügen (anstatt Pseudoelemente zu verwenden). Für den schnellen Zugriff auf das Endergebnis habe ich eine Liste von Codepens zusammengestellt, die Flexbox-Mauerwerk mit 3, 4, 5 und 6 Spalten zeigen .

Da wir mit der Schaffung nur zwei Pseudo-Elemente begrenzt sind :beforeund :afterwir müssen manuell hinzufügen Pause Elemente in unseren Behälter greifen (müssen Sie eine weniger Pause Element als die Anzahl der Spalten im Layout). Sie können sie einfach an das Ende Ihres Containers anhängen. Sie werden in die entsprechenden Spalten sortiert:


...

Wir fügen Spaltenumbrüche als spans ein, um sie unabhängig von den Inhaltselementen zu sortieren. Wir brauchen eine Möglichkeit, die Zählung neu zu starten, sobald wir die Unterbrechungselemente erreicht haben, oder eine ungerade Anzahl von Inhaltselementen könnte zum Beispiel bewirken, dass das erste Unterbrechungselement nach der dritten Spalte beginnt. Der :nth-of-typeSelektor zielt unabhängig auf Elemente unterschiedlichen Typs ab, sodass wir die Reihenfolge der Inhaltselemente und der Spaltenumbrüche wie folgt entkoppeln können:

.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

Die Bremselemente nehmen wie bisher die volle Höhe des Containers ein:

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  margin: 0;
}

Dadurch wird ein Mauerwerkslayout mit vier Spalten erstellt ( Codepen anzeigen ):

1
2
3
4
5
6
7
8
9
10
11
12

Hier ist die vollständige Lösung für ein CSS-Mauerwerkslayout mit vier Spalten:


.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 600px; 
}

.item {
  width:24%;
  margin-bottom: 2%; /* Optional */
}

/* Re-order items into 4 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  margin: 0;
}

Diese CSS-reine Methode zum Erstellen eines Mauerwerk- (oder Mosaik-) Layouts ist sicherlich nicht so robust, flexibel und kinderleicht wie eine JavaScript-Implementierung (wie Masonry ), aber wenn Sie sich nicht nur auf die Erstellung einer Bibliothek eines Drittanbieters verlassen möchten Ein Mauerwerk-Layout Ich hoffe, dass diese Layout-Tricks nützlich sein können.