Nur CSS-Mauerwerkslayout

134

Ich muss ein ziemlich gewöhnliches Mauerwerkslayout implementieren. Aus einer Reihe von Gründen möchte ich jedoch kein JavaScript verwenden, um dies zu tun.

Ein Raster aus mehreren Spalten von Rechtecken unterschiedlicher Höhe.

Parameter:

  • Alle Elemente haben die gleiche Breite
  • Elemente haben eine Höhe, die serverseitig nicht berechnet werden kann (ein Bild plus verschiedene Textmengen)
  • Ich kann mit einer festen Anzahl von Spalten leben, wenn ich muss

Es gibt eine triviale Lösung dafür, die in modernen Browsern funktioniert, die column-countEigenschaft.

Das Problem bei dieser Lösung ist, dass die Elemente in Spalten angeordnet sind:

Ausgehend von der oberen linken Box sind sie direkt nach unten mit 1 bis 4 nummeriert, die oberste Box in der nächsten Spalte ist 5 und so weiter.

Während ich die Elemente in Reihen anordnen muss, zumindest ungefähr:

Ausgehend von der Box ganz oben links sind sie quer von 1 bis 6 nummeriert. Da Box 5 jedoch die kürzeste ist, ist die Box darunter 7, da sie den Anschein hat, in einer Reihe höher als die nächste Box ganz links zu sein.

Ansätze, die ich ausprobiert habe und die nicht funktionieren:

Jetzt könnte ich das serverseitige Rendering ändern und die Elemente neu ordnen, indem ich die Anzahl der Elemente durch die Anzahl der Spalten dividiere. Dies ist jedoch kompliziert und fehleranfällig (basierend darauf, wie Browser die Elementliste in Spalten aufteilen) um es wenn möglich zu vermeiden.

Gibt es eine neue Flexbox-Magie, die dies ermöglicht?

Pekka
quelle
6
Ich kann mir keinen Weg vorstellen, der nicht von vordefinierten Höhen abhängt. Wenn Sie JS noch einmal überdenken, schauen Sie sich stackoverflow.com/questions/13518653/… an, wo ich eine solche Lösung implementiere, die recht einfach ist.
Gabriele Petrioli
1
@Gaby implementiert Ihre Lösung jetzt, danke!
Pekka
4
Mir ist klar, dass Sie nur CSS gesagt haben. Ich möchte nur erwähnen, dass Masonry jQuery nicht mehr benötigt - die minimierte Bibliothek ist unter 8 KB groß - und nur mit HTML initialisiert werden kann. Nur als Referenz jsfiddle.net/wp7kuk1t
I Haz Kode
1
Wenn Sie die Höhe der Elemente im Voraus bestimmen können, indem Sie die Zeilenhöhe, die Schriftgröße (Sie müssten eine bestimmte Schriftart bereitstellen und einige clevere Berechnungen durchführen), die Bildhöhe, den Vertikelrand und den Abstand kennen, können Sie dies mach das. Andernfalls können Sie dies nicht nur mit CSS tun. Sie können auch PhantomJS verwenden, um jedes Element vorab zu rendern und die Höhe dieses Elements zu ermitteln. Es wird jedoch ein erheblicher Overhead / eine erhebliche Latenz hinzugefügt.
Timothy Zorn

Antworten:

157

Flexbox

Ein dynamisches Mauerwerkslayout ist mit flexbox nicht möglich, zumindest nicht sauber und effizient.

Flexbox ist ein eindimensionales Layoutsystem. Dies bedeutet, dass Elemente entlang horizontaler oder vertikaler Linien ausgerichtet werden können. Ein Flex-Element ist auf seine Zeile oder Spalte beschränkt.

Ein echtes Rastersystem ist zweidimensional, dh es kann Elemente entlang horizontaler UND vertikaler Linien ausrichten. Inhaltselemente können sich gleichzeitig über Zeilen und Spalten erstrecken, was Flex-Elemente nicht können.

Aus diesem Grund verfügt die Flexbox nur über eine begrenzte Kapazität zum Aufbau von Gittern. Dies ist auch ein Grund, warum das W3C eine andere CSS3-Technologie entwickelt hat, Grid Layout .


row wrap

In einem Flex-Container mit flex-flow: row wrapmüssen Flex-Elemente in neue Zeilen umbrochen werden .

Dies bedeutet, dass ein Flex-Element nicht unter ein anderes Element in derselben Zeile gewickelt werden kann .

Beachten Sie oben, wie Div # 3 unter Div # 1 umschließt und eine neue Zeile erstellt. Es kann nicht unter div # 2 gewickelt werden .

Wenn die Elemente nicht die höchsten in der Reihe sind, bleibt der Leerraum erhalten, wodurch unschöne Lücken entstehen.


column wrap

Wenn Sie zu wechseln, flex-flow: column wrapist ein gitterartiges Layout besser erreichbar. Ein Container in Säulenrichtung weist jedoch auf Anhieb vier potenzielle Probleme auf:

  1. Flex-Elemente fließen vertikal und nicht horizontal (wie in diesem Fall erforderlich).
  2. Der Container wird horizontal und nicht vertikal erweitert (wie das Pinterest-Layout).
  3. Der Behälter muss eine feste Höhe haben, damit die Gegenstände wissen, wo sie verpackt werden müssen.
  4. Zum jetzigen Zeitpunkt weist es einen Mangel in allen gängigen Browsern auf, in denen der Container nicht erweitert werden kann, um zusätzliche Spalten aufzunehmen .

Infolgedessen ist ein Container in Spaltenrichtung in diesem Fall und in vielen anderen Fällen keine Option.


CSS-Raster mit undefinierten Artikelabmessungen

Das Rasterlayout wäre eine perfekte Lösung für Ihr Problem, wenn die verschiedenen Höhen der Inhaltselemente im Voraus festgelegt werden könnten . Alle anderen Anforderungen liegen innerhalb der Kapazität von Grid.

Die Breite und Höhe von Rasterelementen muss bekannt sein, um Lücken mit umgebenden Elementen zu schließen.

Daher ist Grid, das beste CSS-Angebot für den Bau eines horizontal fließenden Mauerwerks, in diesem Fall nicht ausreichend.

Bis eine CSS-Technologie mit der Fähigkeit zum automatischen Schließen der Lücken eintrifft, hat CSS im Allgemeinen keine Lösung. So etwas würde wahrscheinlich ein erneutes Fließen des Dokuments erfordern, daher bin ich mir nicht sicher, wie nützlich oder effizient es sein würde.

Du brauchst ein Skript.

JavaScript-Lösungen verwenden in der Regel die absolute Positionierung, bei der Inhaltselemente aus dem Dokumentfluss entfernt werden, um sie ohne Lücken neu anzuordnen. Hier sind zwei Beispiele:


CSS-Raster mit definierten Artikelabmessungen

Für Layouts, bei denen die Breite und Höhe von Inhaltselementen bekannt sind, finden Sie hier ein horizontal fließendes Mauerwerkslayout in reinem CSS:

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}
<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

jsFiddle-Demo


Wie es funktioniert

  1. Richten Sie einen Rastercontainer auf Blockebene ein. ( inline-gridwäre die andere Option)
  2. Die grid-auto-rowsEigenschaft legt die Höhe automatisch generierter Zeilen fest. In diesem Raster ist jede Reihe 50 Pixel groß.
  3. Das grid-gapAnwesen ist eine Abkürzung für grid-column-gapund grid-row-gap. Diese Regel legt eine 10-Pixel-Lücke zwischen Rasterelementen fest . (Dies gilt nicht für den Bereich zwischen Elementen und dem Container.)
  4. Die grid-template-columnsEigenschaft legt die Breite explizit definierter Spalten fest.

    Die repeatNotation definiert ein Muster sich wiederholender Spalten (oder Zeilen).

    Die auto-fillFunktion weist das Raster an, so viele Spalten (oder Zeilen) wie möglich auszurichten, ohne den Container zu überlaufen. (Dies kann zu einem ähnlichen Verhalten wie bei Flex-Layouts führen flex-wrap: wrap.)

    Die minmax()Funktion legt einen minimalen und maximalen Größenbereich für jede Spalte (oder Zeile) fest. Im obigen Code beträgt die Breite jeder Spalte mindestens 30% des Containers und höchstens den verfügbaren freien Speicherplatz.

    Die frEinheit repräsentiert einen Bruchteil des freien Speicherplatzes im Gittercontainer. Es ist vergleichbar mit der flex-growEigenschaft von Flexbox .

  5. Mit grid-rowund spansagen wir den Rasterelementen, über wie viele Zeilen sie sich erstrecken sollen.


Browser-Unterstützung für CSS Grid

  • Chrome - volle Unterstützung ab 8. März 2017 (Version 57)
  • Firefox - volle Unterstützung ab 6. März 2017 (Version 52)
  • Safari - volle Unterstützung ab 26. März 2017 (Version 10.1)
  • Edge - volle Unterstützung ab 16. Oktober 2017 (Version 16)
  • IE11 - keine Unterstützung für aktuelle Spezifikation; unterstützt veraltete Version

Hier ist das vollständige Bild: http://caniuse.com/#search=grid


Coole Grid-Overlay-Funktion in Firefox

Wenn Sie in Firefox-Entwicklungstools den Rastercontainer überprüfen, befindet sich in der CSS-Deklaration ein winziges Rastersymbol. Beim Klicken wird auf der Seite ein Umriss Ihres Rasters angezeigt.

Weitere Details finden Sie hier: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

Michael Benjamin
quelle
5
Fantastische Antwort! Ist es mit der Lösung "CSS-Raster mit definierten Elementabmessungen" möglich, die Höhe einer Zelle als Prozentsatz der Zellenbreite auszudrücken? Dies wäre nützlich für die Anzeige von Bildern, bei denen das Seitenverhältnis bekannt ist. Wir wollen das Seitenverhältnis jederzeit beibehalten.
Oliver Joseph Ash
Vielen Dank. In Bezug auf das Problem des Seitenverhältnisses für Bilder, die Methode "Prozentsatz der Zellenbreite" (der Trick zum Auffüllen des Prozentsatzes?), Ist sie in Grid oder Flexbox nicht zuverlässig. Sehen Sie hier und hier . @OliverJosephAsh
Michael Benjamin
Ja, ich beziehe mich auf den prozentualen Polstertrick. Ist es möglich, eine der Problemumgehungen in Kombination mit Ihrer Mauerwerkslayoutlösung zu verwenden? Für den Kontext möchten wir ein Nur-CSS-Mauerwerkslayout für die Bilder auf unsplash.com implementieren .
Oliver Joseph Ash
1
Leider ist die Verwendung von JS hierfür keine Option. Wir möchten das serverseitige Rendern aus Leistungs- und SEO-Gründen aktivieren. Dies bedeutet, dass das Layout gerendert werden muss, bevor clientseitiges JavaScript heruntergeladen, analysiert und ausgeführt wurde. Angesichts der Tatsache, dass dies unmöglich zu sein scheint, müssen wir wohl irgendwo auf der Strecke einen Kompromiss eingehen! Vielen Dank für Ihre Hilfe :-)
Oliver Joseph Ash
1
Spezifikationsaktualisierung : In Flexbox werden jetzt prozentuale Ränder und Auffüllungen festgelegt, um die Inline-Größe des Containers erneut aufzulösen. Drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin
13

Dies ist eine kürzlich entdeckte Technik mit Flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

Der Artikel macht für mich Sinn, aber ich habe nicht versucht, ihn zu verwenden, daher weiß ich nicht, ob es irgendwelche Einschränkungen gibt, die nicht in Michaels Antwort erwähnt sind.

Hier ist ein Beispiel aus dem Artikel, in dem die orderEigenschaft verwendet wird, kombiniert mit:nth-child .

Stapelschnipsel

.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: 960px;
  
  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 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;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>

Oliver Joseph Ash
quelle
Zunächst einmal sind nur Link-Antworten schlecht, wie wenn ein solcher Link stirbt, auch die Antwort. Zweitens, wenn Sie die gegebene Antwort genauer lesen, werden Sie feststellen, dass das, was in Ihrem Link erwähnt wird, abgedeckt ist. Einfach ausgedrückt, es gibt zu viele Einschränkungen bei der Verwendung von Flexbox allein, es sei denn, das oben Genannte ist kein Problem.
Ason
3
Ich habe die akzeptierte Antwort sehr gründlich gelesen - Sie werden sogar einige Kommentare sehen, die ich unten hinzugefügt habe. Der Flexbox-Ansatz, mit dem ich verknüpft habe, ist einzigartig und wird von dieser Antwort nicht abgedeckt. Ich möchte den Artikel nicht wiederholen, weil er sehr kompliziert ist, und der Artikel leistet einen großartigen Beitrag dazu.
Oliver Joseph Ash
Das einzige, was der Link anders macht, ist die Verwendung der orderEigenschaft, sodass sie so aussieht, als würden Elemente von links nach rechts fließen. Dennoch ist sein Haupttrick flex-flow: column wrap, der in der obigen Antwort erwähnt wird. Außerdem kann es die Elemente nicht so fließen lassen, wie es ein echtes Mauerwerk tut, z. B. wenn das 3. und 4. Element in die 3. Spalte passen würde, würden sie es tun, das verknüpfte nicht. Aber wie gesagt, alles hängt von den Anforderungen ab, und in diesem Fall (dieser Frage) funktioniert es nicht so, wie es verlangt wurde.
Ason
Außerdem geht es nicht um "Sie möchten den Artikel nicht wiederholen" , eine Antwort sollte den wesentlichen Teil des Codes enthalten, damit sie auch dann gültig bleibt, wenn die verknüpfte Ressource stirbt.
Ason
3
Ich habe es aktualisiert, ein Beispiel aus Artikel + eine positive Bewertung hinzugefügt.
Ason