IE10-kompatible Grid-Auto-Platzierung mit Flexbox

Avatar of Brian Holt
Brian Holt am

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

Wenn Sie an Webanwendungen arbeiten, die ältere Browser unterstützen, und von der Seitenlinie aus nach CSS Grid gelüstet haben, wie ich, habe ich gute Nachrichten: Ich habe einen cleveren, reinen CSS-Weg entdeckt, um die Grid-Auto-Platzierung in IE10+ zu nutzen!

Nun, es ist nicht *wirklich* CSS Grid, aber wenn man sich den Code nicht ansieht, würde man es nicht merken. Die HTML-Struktur sieht aus wie CSS Grid. Sie hat eine definierte Anzahl von Spalten mit einer undefinierten Anzahl von Zeilen und sie hat Abstände, die Ränder und Schatten auf den Zellen ohne Hacks unterstützen. Aber was tatsächlich hinter den Kulissen passiert, ist eine Kombination aus Flexbox und Margins.

In diesem Artikel werde ich den Ansatz Schritt für Schritt erläutern. Hier ist eine Demo dessen, was wir uns ansehen

Siehe den Stift
IE10-kompatibles CSS-Grid-ähnliches Spaltenlayout
von Brian Holt (@bholtbholt)
auf CodePen.

Automatisch fließende Zeilen mit Flexbox-Wrap

Five orange rectangles in two rows with three on the first row and two on the second row.
Flexbox-erstelltes Auto-Placement-Grid

Das grundlegende Grid-Setup ist sehr einfach. Wenn Sie mit Flexbox vertraut sind, haben Sie sich sicher schon gedacht, dass flex-wrap: wrap der Trick ist. Und Sie hätten Recht.

Lassen Sie uns zuerst das HTML-Markup erstellen, bevor wir CSS schreiben. Wir möchten, dass es der gleichen Struktur ähnelt, als würden wir Auto-Placement verwenden – ein .grid-Container und eine undefinierte Anzahl von .grid__cells.

<div class="grid">
  <div class="grid__cell">...</div>
  ...
</div>

Wir legen drei Grid-Breakpoints fest. Ein einspaltiges, zweispaltiges und dreispaltiges Layout für Mobilgeräte, kleine Bildschirme und mittlere Bildschirme. Ich verwende die Breakpoints, die in Bootstrap verwendet werden, obwohl wir sie an den tatsächlichen Stellen definieren müssten, an denen das Layout bricht, wenn wir mit realen Inhalten arbeiten würden.

$screen-sm-min: 768px;
$screen-sm-max: 991px;
$screen-md-min: 992px;
Five orange rectangles stacked on top of one another.
Mobile-first Grid bricht auf eine einzelne Spalte zusammen

Ein Mobile-first-Ansatz bedeutet, dass unser einspaltiges Layout bereits fertig ist, da jeder .grid__cell bereits ein Block ist. Wir setzen .grid so, dass es nach dem ersten Breakpoint zu einem Flexbox-Container wird und die Zellen umbricht.

@media (min-width: $screen-sm-min) {
  .grid {
    display: flex;
    flex-wrap: wrap;
  }
}

Unsere zwei- und dreispaltigen Layouts benötigen explizite Breiten und Flex-Eigenschaften; sonst werden sie auf einer einzigen Zeile gequetscht. Beim Testen von IE10 stieß ich auf unerwartetes Verhalten mit der flex-basis-Eigenschaft und stellte fest, dass das Festlegen einer expliziten Breite mit flex-basis: auto konsistenter war. Dies schien jedoch kein Problem mit IE11 zu sein.

.grid__cell {
  min-width: 0;
  flex: 1 1 auto;
}

// Two-column grid
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
  $width: 50%;

  .grid__cell {
    width: $width;
  }
}

// Three-column grid
@media (min-width: $screen-md-min) {
  $width: 33.33%;

  .grid__cell {
    width: $width;
  }
}

Wir müssen .grid__cell nicht in einer Media Query verpacken, da seine Flex-Eigenschaften keine Wirkung haben, wenn das übergeordnete Element kein Flexbox-Container ist. Wir definieren auch eine Obergrenze für die zweispaltige Media Query, damit sie das dreispaltige Grid nicht beeinflusst.

Und das ist es! Wir haben jetzt ein responsives, flüssiges, umbrechendes Flexbox-Grid. Der einfache Teil ist erledigt ... na ja, solange wir immer nur Elemente haben, die Vielfache von zwei und drei sind. Mit flex: 1 1 auto nimmt das letzte Element immer den verbleibenden Platz in der letzten Zeile ein.

Three rows of orange rectangles. The first two rows have two columns of boxes and the third row has one single box that spans both columns.
Zweispaltiges Grid auf kleineren Bildschirmen
Two rows of orange rectangles. The first row has three columns of rectangles and the second row has two rectnagles that span the full width.
Dreispaltiges Grid auf großen Bildschirmen

Ausrichten von Zellen in der letzten Zeile

Die schwer fassbare letzte Zeile ist der Grund, warum wir hier sind, oder? Standardmäßig dehnt sich jede Zelle in einer Flexbox-Layouts bis zum Ende der Zeile aus, aber Grid hinterlässt einen leeren Fleck. Wie machen wir das in Flexbox? Mit Pseudoelementen!

Der Trick besteht darin, dem .grid-Container ein Pseudoelement hinzuzufügen und es wie eine Zelle zu konfigurieren. Wir definieren die :after Pseudoelement-Zelle an jedem unserer Breakpoints mit der gleichen Breite wie eine echte Zelle.

@media (min-width: $screen-sm-min) {
  .grid {
    ...

    &:after {
      content: '';
      display: block;
      flex: 1 1 auto;
    }
  }
}

@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
  $width: 50%;

  .grid:after {
    width: $width;
  }
}

@media (min-width: $screen-md-min) {
  $width: 33.33%;

  .grid:after {
    width: $width;
  }
}

Dies erzeugt eine gefälschte Zelle, die gegen unsere echten Zellen drückt und unser zweispaltiges Grid ausrichtet, wenn die Zellen ungerade sind. Wenn wir ihre Höhe undefiniert lassen, kann sie kollabieren, wenn die Zellen gerade sind.

Three rows of orange rectangles. First two rows have two columns, each with a rectangle. Third row has a single rectangle and an empty column.
Zweispaltiges Grid mit ungeraden Zellen, die sich einfügen

Unser dreispaltiges Grid ist etwas komplexer, da wir mehrere Zustände handhaben müssen, wie z. B. wenn es eine leere Zelle gibt und wenn es zwei leere Zellen gibt.

Three column grid of orange rectangles with two rows. The second row only has one rectangle and an empty column.
Dreispaltiges Grid mit einer leeren Zelle

Unser Zustand mit einer leeren Zelle ist bereits abgedeckt, da er sich nicht wirklich von einer leeren Zelle in zwei Spalten unterscheidet. Die :after-Zelle hat ihre Breite eingestellt und vervollständigt die Zeile. Die Geschichte ändert sich jedoch, wenn es zwei leere Zellen gibt, denn flex: 1 1 auto taucht wieder auf: Die letzte Zelle erstreckt sich nun über 50 % der Breite, wenn sie gegen das Pseudoelement gedrückt wird.

Three column grid of orange rectangles with two rows. The second row has one rectangle that spans half the grid width leaving an empty white space.
Dreispaltiges Grid mit zwei leeren Zellen

Mithilfe von CSS :nth-of-type-Selektoren können wir die erste Spalte in jeder Zeile ansprechen. Da unsere Zeilen Vielfache von drei sind, sprechen wir sie mit 3n an und zählen dann rückwärts um 2, um das erste Element in jeder Zeile zu erhalten.

@media (min-width: $screen-md-min) {
  .grid__cell {
    ...

    &:nth-of-type(3n-2) {
      background-color: red;
    }
  }
}
Three column grid of rectangles. The first column of rectangles is red indicating the rectangles that are selected with CSS. The other rectangles are orange.
Ansprechen der ersten Zelle in jeder dreispaltigen Zeile

Wir sprechen grob alle Zellen in der ersten Spalte an, müssen die Auswahl aber auf die letzte Zeile beschränken. Tatsächlich müssen wir sie darauf beschränken, wenn es die letzte Zelle in der ersten Spalte der letzten Zeile ist. Glücklicherweise gibt es einen praktischen Pseudoselektor, um das letzte Element seiner Art anzusprechen. Wir verketten :last-of-type, um die logische Anweisung zu erstellen.

@media (min-width: $screen-md-min) {
  .grid__cell {
    ...
    &:nth-of-type(3n-2):last-of-type {
      background-color: red;
    }
  }
}

Nachdem wir nun die letzte Zelle in der ersten Spalte der letzten Zeile ausgewählt haben, verwenden wir einen Margin, um die :after-Zelle in die letzte Spalte zu verschieben und die mittlere Zelle zu füllen.

@media (min-width: $screen-md-min) {
  .grid__cell {
    ...

    &:nth-of-type(3n-2):last-of-type {
      margin-right: $width;
    }
  }
}

Hier ist unser mit Flexbox definierter Auto-Placement-Grid-Imitator im Ganzen. Betrachten Sie seine wunderschön ausgerichteten Zeilen. Ich wette, Sie können nicht einmal sagen, dass es nicht CSS Grid ist!

Three column grid with three rows of orange rectangles. The last row has a single rectangle in the first column and the other two columns are empty.
Unser vollständiges dreispaltiges Grid.

Abstände mit Margins hinzufügen

Die Spezifikation von CSS Grid hat eine Spalten- und Zeilenlücke, um Abstand zwischen jeder Zelle zu schaffen. Das Erstellen von Abständen in Flexbox ist viel schwieriger. Es sieht so aus, als würde es in Flexbox kommen, aber wir sind noch nicht so weit ... und IE wird es nie sein.

In Daniel Tonons Anleitung zu CSS Grid in IE verwendete er ein inneres Zellen-Div mit negativen Margins, Rändern, etwas Padding und overflow: hidden. Obwohl es vielleicht etwas hacky ist, funktioniert der Effekt, aber er verletzt unser Bestreben, eine CSS Grid-ähnliche HTML-Struktur beizubehalten. Der von mir bevorzugte Ansatz mag etwas roh erscheinen, aber ich fand ihn auch am einfachsten zu lesen und zu verstehen. Darüber hinaus verwendet er weiterhin :nth-of-type Pseudoselektoren, was den Gesamtansatz konsistent erscheinen lässt.

Wir möchten Lücken zwischen den Zellen, aber nicht außen herum. Wir möchten auch, dass unsere Zellen bündig mit dem Container abschließen.

A three-by-two grid of orange rectangles. A block arrow is pointing at a gap between the rectangles.
Lücken zwischen den Zellen, nicht außen.

Unser mobiles oder einspaltiges Grid benötigt nur einen unteren Margin an den Zellen. Wir fügen diesen hinzu und überschreiben die allerletzte Zelle mit margin-bottom: 0, damit die Zelle bündig mit dem Container abschließt. Normalerweise würde ich initial verwenden, aber es gibt keine Unterstützung in IE.

$col-gap: 16px;

.grid__cell {
  ...
  margin-bottom: $col-gap;

  &:last-of-type {
    margin-bottom: 0;
  }
}
A single column of orange rectangles in five rows.
Einspaltiges Grid mit Lücken zwischen jeder Zeile

Unsere zwei- und dreispaltigen Grids benötigen Margins auf der rechten Seite der Zellen, keine rechten Margins in der letzten Spalte und keine unteren Margins auf irgendeiner der Zellen der letzten Zeile. Aufgrund der Margins müssen wir auch unsere Breiten neu berechnen, da die Zellen umbrechen, wenn sie nicht passen.

In einem zweispaltigen Layout ist das Erreichen der rechten (oder zweiten) Spalte mit :nth-of-type(2n) oder :nth-of-type(even) recht einfach. Ich bevorzuge einen n-Multiplikator für die Konsistenz mit unserem dreispaltigen Grid und zur Berechnung der letzten Zeile.

Unsere letzte Zeile ist etwas kniffliger. Wenn wir ungerade Zellen haben, kümmert sich unser Mobile-first-CSS darum, die unteren Margins zu entfernen, da die Zelle die :last-of-type ist und unsere :after-Zelle keine Margins angewendet bekommt.

A two-by-three grid of orange rectangles. The last rectangle is a little taller than the others.
Zweispaltiges Grid mit geraden Zellen

Wenn wir gerade Zellen haben, müssen wir die vorletzte Zelle ansprechen, aber nur, wenn sie sich in der ersten Spaltenposition befindet. Wenn wir sie nicht qualifizieren würden, würde die vorletzte Zelle vertikal wachsen, um der Höhe der vorletzten Zeile zu entsprechen. Wir können sie mit :nth-of-type(2n-1):nth-last-of-type(2) ansprechen.

@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
  $width: calc(50% - #{$col-gap});

  .grid__cell {
    ...
    margin-right: $col-gap;

    // Remove margin in last column
    &:nth-of-type(2n) {
      margin-right: 0;
    }

    // For when the last row is complete
    // . .
    // * .
    &:nth-of-type(2n-1):nth-last-of-type(2) {
      margin-bottom: 0;
    }
  }
}
The same two-by-three grid as before, but with the last rectangle at an even height with the rest.
Zweispaltiges Grid mit geraden Zellen, die bündig mit dem Container abschließen

Unsere dreispaltigen Abstände folgen dem gleichen Ansatz. Wir fügen allen einen margin-right hinzu, entfernen ihn aus der dritten Spalte und entfernen die unteren Margins aus der letzten Zeile. Wieder einmal wird unsere letzte Zelle durch unseren Mobile-first-Ansatz abgedeckt, aber jetzt müssen wir abdecken, wenn es zwei Zellen in der letzten Zeile gibt und wenn es drei Zellen gibt. Wir können unsere Selektoren mit nth-of-type und nth-last-of-type qualifizieren.

@media (min-width: $screen-md-min) {
  $width: calc(33% - #{$col-gap});

  .grid__cell {
    ...
    margin-right: $col-gap;

    // Remove margin in last column
    &:nth-of-type(3n) {
      margin-right: 0;
    }

    // For when there two items in the last row
    // . . .
    // * .
    &:nth-of-type(3n-2):nth-last-of-type(2) {
      margin-bottom: 0;
    }

    // For when the last row is complete
    // . . .
    // * * .
    &:nth-of-type(3n-1):nth-last-of-type(2),
    &:nth-of-type(3n-2):nth-last-of-type(3) {
      margin-bottom: 0;
    }
  }
}
A three-by-three grid of orange rectangles, with the last cell empty.
Dreispaltiges Grid mit Abständen und einer leeren Zelle

Wir müssen den Margin der letzten Zelle in der letzten Zeile anpassen, wenn sie aufgrund der Spalten allein ist. Wir verwenden 33 % plus einen Rand auf jeder Seite.

@media (min-width: $screen-md-min) {
  $width: calc(33% - #{$col-gap});

  .grid__cell {
    ...
    // When there is only one item in the last rpw
    // Fill the margin so it's like the last item is
    // double the width
    // . . .
    // *->
    &:nth-of-type(3n-2):last-of-type {
      margin-right: calc(33% + #{$col-gap * 2});
    }
  }
}

Jetzt sind unsere Abstände installiert und das Grid ist komplett! Füllen Sie sie mit Rändern, Schatten oder was immer Ihr Herz begehrt.

A three-by-two grid of orange rectangles with the last cell empty.
Vollständiges dreispaltiges Grid mit Abständen, das Flexbox verwendet.

Zusammenfassung

Hier ist noch einmal das Endergebnis

Siehe den Stift
IE10-kompatibles CSS-Grid-ähnliches Spaltenlayout
von Brian Holt (@bholtbholt)
auf CodePen.

Ich glaube, diese Technik könnte mit kleineren Anpassungen auch IE9 unterstützen, z. B. durch die Verwendung von Inline-Blocks anstelle von Flexbox. Wir könnten auch zu einem vierspaltenen Grid erweitern, indem wir einen weiteren Breakpoint hinzufügen und den gleichen Ansatz wie beim dreispaltigen Grid verwenden. Fühlen Sie sich frei, diesen Ansatz zu verwenden, und ich hoffe, er hilft!