Wie man CSS Grid für Sticky Header und Footer verwendet

Avatar of Adam Rackis
Adam Rackis am

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

CSS Grid ist eine Sammlung von Eigenschaften, die das Layout einfacher denn je machen. Wie bei allem gibt es eine kleine Lernkurve, aber Grid macht ehrlich gesagt Spaß, wenn man den Dreh erst einmal raus hat. Ein Bereich, in dem es glänzt, ist der Umgang mit Headern und Footern. Mit einer kleinen Anpassung unseres Denkens können wir Header und Footer so gestalten, dass sie sich wie feststehend verhalten oder die "Sticky"-Behandlung erhalten (nicht position: sticky, aber die Art von Footer, die am unteren Bildschirmrand klebt, auch wenn nicht genügend Inhalt vorhanden ist, um sie dorthin zu schieben, und die bei mehr Inhalt weggedrückt wird). 

Hoffentlich weckt dies weiteres Interesse an modernen Layouts, und wenn ja, kann ich Rachel Andrews Buch The New CSS Layout nicht genug empfehlen: Es behandelt die beiden wichtigsten modernen Layout-Techniken, Grid und Flexbox.

Was wir machen

Lassen Sie uns ein ziemlich klassisches HTML-Layout implementieren, das aus einem Header, einem Hauptinhalt und einem Footer besteht.

Wir werden einen wirklich festen Footer erstellen, der am unteren Rand des Viewports bleibt, während sich der Hauptinhalt bei Bedarf darin scrollt, und ihn dann später zu einem traditionelleren Sticky-Footer aktualisieren, der am unteren Rand des Viewports beginnt, auch wenn der Hauptinhalt klein ist, aber bei Bedarf weggedrückt wird. Darüber hinaus werden wir, um unsere Kenntnisse von Grid zu erweitern, unseren Hauptinhaltscontainer so gestalten, dass er entweder die gesamte Breite des Viewports einnimmt oder einen schön zentrierten Streifen in der Mitte beansprucht.

Ein fester Footer ist etwas ungewöhnlich. Footer werden üblicherweise so gestaltet, dass sie am unteren Rand des Viewports beginnen und bei Bedarf vom Hauptinhalt nach unten gedrückt werden. Aber ein permanenter Footer ist nicht unbekannt. Charles Schwab macht das auf ihrer Homepage. So oder so, es wird Spaß machen, es zu implementieren!

Aber bevor wir weitermachen, können Sie sich gerne den festen Footer auf der Charles Schwab-Website ansehen. Nicht überraschend verwendet er feste Positionierung, was bedeutet, dass er eine fest codierte Größe hat. Tatsächlich, wenn wir DevTools öffnen, sehen wir, dass gleich zu Beginn

body #qq0 {
  border-top: 4px solid #133568;
  background-color: #eee;
  left: 0;
  right: 0;
  bottom: 0;
  height: 40px!important;
}

Nicht nur das, sondern es gibt auch die Balance, sicherzustellen, dass der Hauptinhalt nicht hinter diesem festen Footer verborgen wird, was durch das Setzen von fest codierten Abständen (einschließlich 15px am unteren Rand des <footer>-Elements), Rändern (einschließlich 20px bei <ul> im Footer) und sogar Zeilenumbrüchen erreicht wird.

Versuchen wir, das ohne diese Einschränkungen hinzubekommen.  

Unsere Basisstile

Lassen Sie uns ein minimales UI skizzieren, um uns den Einstieg zu erleichtern, und dann unser Grid an unsere Ziele anpassen. Unten finden Sie einen CodeSandbox, plus zusätzliche für die nachfolgenden Schritte, die uns zum Endergebnis führen.

Zuerst machen wir ein paar Vorarbeiten. Wir stellen sicher, dass wir die gesamte Höhe des Viewports nutzen, damit wir beim Hinzufügen unseres Grids den Footer einfach am unteren Rand platzieren können (und dort halten). Es wird nur ein Element im <body> des Dokuments mit der ID #app geben, das die Elemente <header, <main> und <footer> enthalten wird.

body {
  margin: 0; /* prevents scrollbars */
}


#app {
  height: 100vh;
}

Als Nächstes richten wir unsere Header-, Main- und Footer-Bereiche sowie das Grid ein, in dem sie alle platziert werden. Um klar zu sein, dies wird von Anfang an nicht so funktionieren, wie wir es wollen. Es dient nur als Ausgangspunkt, auf dem wir aufbauen können.

body {
  margin: 0;
}


#app {
  height: 100vh;
  
  /* grid container settings */
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas: 
    'header'
    'main'
    'footer';
}


#app > header {
  grid-area: header;
}


#app > main {
  grid-area: main;
  padding: 15px 5px 10px 5px;
}


#app > footer {
  grid-area: footer;
}

Wir haben ein einfaches einspaltiges Layout mit einer Breite von 1fr erstellt. Wenn Ihnen 1fr neu ist, bedeutet es im Wesentlichen "den verbleibenden Platz einnehmen", was in diesem Fall die gesamte Breite des Grid-Containers #app ist.

Wir haben auch drei Zeilen definiert.

#app {
  /* etc. */
  grid-template-rows: auto 1fr auto;
  /* etc. */
}

Die erste und dritte Zeile, die jeweils unser Header und Footer sein werden, sind mit `auto` bemessen, was bedeutet, dass sie so viel Platz einnehmen, wie benötigt wird. Mit anderen Worten: keine fest codierten Größen erforderlich! Dies ist ein äußerst wichtiges Detail und ein perfektes Beispiel dafür, wie wir von der Verwendung von CSS Grid profitieren.

Die mittlere Zeile ist dort, wo wir unseren Inhalt platzieren werden. Wir haben ihr die Größe 1fr zugewiesen, was wieder bedeutet, dass sie den gesamten verbleibenden Platz einnimmt, der von den anderen beiden Zeilen übrig bleibt. Wenn Sie sich fragen, warum wir sie nicht auch auf auto setzen, liegt das daran, dass das gesamte Grid die gesamte Höhe des Viewports umfasst, sodass wir einen Bereich benötigen, der wächst und jeden ungenutzten Platz ausfüllt. Beachten Sie, dass wir keine festen Höhen, Ränder, Abstände - oder gar Zeilenumbrüche! - haben oder jemals benötigen werden, um Dinge in Position zu bringen. Das ist das gute Leben, wenn man mit grid arbeitet!

Versuchen wir es mit Inhalt?

Sie werden in der Sandbox bemerken, dass ich React verwendet habe, um diese Demo zu erstellen, aber da dies kein Beitrag über React ist, werde ich mich nicht lange mit diesen Details aufhalten; React hat absolut nichts mit der CSS Grid-Arbeit in diesem Beitrag zu tun. Ich benutze es nur als einfache Möglichkeit, zwischen verschiedenen Markup-Blöcken zu navigieren. Wenn Sie React hassen, ist das in Ordnung: hoffentlich können Sie es in diesem Beitrag ignorieren.

Wir haben Header, Main und Footer Komponenten, die die erwarteten <header, <main>  und <footer> Elemente rendern. Und natürlich sitzt das alles in unserem #app Container. Ja, theoretisch sollte #app semantisch gesehen ein <article> Element sein, aber das sah für mich immer seltsam aus. Ich wollte diese Details nur vermitteln, damit wir alle auf dem gleichen Stand sind, während wir voranschreiten.

Für den eigentlichen Inhalt habe ich Rechnungs- und Einstellungenbereiche, zwischen denen Sie im Header navigieren können. Beide rendern gefälschte, statische Inhalte und sind nur dazu gedacht, unser Layout in Aktion zu zeigen. Der Einstellungenbereich wird der Inhalt sein, den wir in einen zentrierten Streifen auf unserer Seite einfügen, die Rechnungsstellung wird der sein, der unsere gesamte Seite überspannt.

Hier ist die Sandbox mit dem bisherigen Stand.

Der Rechnungsbereich sieht gut aus, aber der Einstellungenbereich drückt unseren Footer aus dem Bild. Nicht nur das, sondern wenn wir scrollen, scrollt die gesamte Seite, wodurch wir unseren Header verlieren. Das mag in manchen Fällen wünschenswert sein, aber wir möchten, dass sowohl der Header als auch der Footer sichtbar bleiben, also lassen Sie uns das beheben.

Als wir unser Grid ursprünglich einrichteten, gaben wir ihm eine Höhe von 100vh, was die gesamte Höhe des Viewports ist. Dann wiesen wir den Zeilen für den Header und den Footer eine automatische Höhe zu und dem Hauptbereich eine Höhe von 1fr, um den verbleibenden Platz einzunehmen. Leider, wenn der Inhalt den verfügbaren Platz übersteigt, dehnt er sich über die Grenzen des Viewports hinaus aus und drückt unseren Footer nach unten und aus dem Blickfeld.

Die Lösung ist trivial: Das Hinzufügen von overflow: auto bewirkt, dass unser <main>-Element scrollt, während unsere <header>- und <footer>-Elemente an Ort und Stelle bleiben.

#app > main {
  grid-area: main;
  overflow: auto;
  padding: 15px 5px 10px 5px;
}

Hier ist die aktualisierte Demo, die dies nutzt.

Verstellbarer Breiten-Hauptbereich

Wir möchten, dass unser <main>-Element entweder die gesamte Breite des Viewports einnimmt oder in einem 600-Pixel-Bereich zentriert wird. Sie denken vielleicht, dass wir <main> einfach eine feste Breite von 600 Pixeln mit automatischen Rändern auf beiden Seiten geben könnten. Aber da dies ein Beitrag über Grid ist, verwenden wir mehr Grid. (Außerdem wird, wie wir später sehen werden, eine feste Breite sowieso nicht funktionieren).

Um unser zentriertes 600-Pixel-Element zu erreichen, machen wir das <main>-Element tatsächlich zu einem Grid-Container. Richtig, ein Grid im Grid! Verschachtelte Grids sind ein absolut legitimer Ansatz und werden in Zukunft einfacher, wenn Subgrid offiziell in allen Browsern unterstützt wird. In diesem Szenario machen wir <main> zu einem Grid mit drei Spaltenspuren von 1fr 600px 1fr oder, einfacher gesagt, 600 Pixel in der Mitte, mit dem verbleibenden Platz gleichmäßig an den Seiten aufgeteilt.

#app > main {
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: 1fr 600px 1fr;
}

Nun positionieren wir unseren Inhalt im Grid. Unsere verschiedenen Module rendern alle in einem <section>-Kindelement. Nehmen wir an, dass der Inhalt standardmäßig den mittleren Bereich einnimmt, es sei denn, er hat eine .full-Klasse, in diesem Fall überspannt er die gesamte Grid-Breite. Wir werden hier keine benannten Bereiche verwenden und stattdessen präzise Grid-Koordinaten im Format [row-start] / [col-start] / [row-end] / [col-end] angeben.

#app > section {
  grid-area: 1 / 2 / 1 / 3;
}


#app > section.full {
  grid-area: 1 / 1 / 1 / 4
}

Sie wundern sich vielleicht über einen col-end-Wert von 4, obwohl es nur drei Spalten gibt. Das liegt daran, dass die Spalten- und Zeilenwerte Spalten- und Zeilen-Gridlinien sind. Es braucht vier Gridlinien, um drei Grid-Spalten zu zeichnen. 

Unser <section> wird immer in der ersten Zeile, der einzigen Zeile, liegen. Standardmäßig überspannt es die Spaltengrenzen 2 bis 3, was die mittlere Spalte ist, es sei denn, die Sektion hat eine full-Klasse, in diesem Fall überspannt sie die Spaltengrenzen 1 bis 4, was allen drei Spalten entspricht.

Hier ist eine aktualisierte Demo mit diesem Code. Sie wird wahrscheinlich gut aussehen, abhängig von Ihrem CodeSandbox-Layout, aber es gibt immer noch ein Problem. Wenn Sie die Anzeige auf kleiner als 600 Pixel verkleinern, wird der Inhalt abrupt abgeschnitten. Wir wollen keine feste Breite von 600 Pixeln in der Mitte. Wir wollen eine Breite von bis zu 600 Pixeln. Es stellt sich heraus, dass Grid genau das richtige Werkzeug dafür hat: die minmax()-Funktion. Wir geben eine minimale und eine maximale Breite an, und das Grid berechnet einen Wert, der in diesem Bereich liegt. So verhindern wir, dass der Inhalt aus dem Grid "herausplatzt".

Wir müssen nur den 600px-Wert durch minmax(0, 600px) ersetzen.

main {
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: 1fr minmax(0, 600px) 1fr;
}

Hier ist die Demo für den fertigen Code.

Zuvor haben wir verhindert, dass der Footer vom Bildschirm weggedrückt wird, indem wir die overflow-Eigenschaft des <main>-Elements auf auto gesetzt haben.

Aber wie wir kurz angedeutet haben, könnte das ein wünschenswerter Effekt sein. Tatsächlich ist es eher ein klassischer "Sticky"-Footer, der dieses lästige Problem löst und den Footer am unteren Rand des Viewports platziert, wenn der Inhalt sehr kurz ist.

Hey, zurück nach unten!

Wie könnten wir all unsere bestehende Arbeit beibehalten, aber zulassen, dass der Footer heruntergedrückt wird, anstatt sich am unteren Rand zu fixieren?

Derzeit ist unser Inhalt in einem Grid mit folgender HTML-Struktur:

<div id="app">
  <header />
  <main>
    <section />
  </main>
  <footer />
</div>

...wobei <main> ein Grid-Container ist, der in den #app Grid-Container verschachtelt ist, der eine Zeile und drei Spalten enthält, die wir zur Positionierung der Modulinhalte verwenden, die sich im <section>-Tag befinden.

 Lassen Sie uns das zu diesem ändern:

<div id="app">
  <header />
  <main>
    <section />
    <footer />
  </main>
</div>

...und integrieren Sie <footer> in das Grid des <main>-Elements. Wir beginnen damit, unser übergeordnetes #app-Grid zu aktualisieren, sodass es nun aus zwei statt drei Zeilen besteht.

#app {
  /* same as before */


  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas: 
    'header'
    'main';
}

Nur zwei Zeilen, eine für den Header und die andere für alles andere. Nun aktualisieren wir das Grid in unserem <main>-Element.

#app > main {
  display: grid;
  grid-template-rows: 1fr auto;
  grid-template-columns: 1fr minmax(0, 600px) 1fr;
}

Wir haben eine neue automatisch bemessene Zeile eingeführt. Das bedeutet, wir haben nun eine 1fr-Zeile für unseren Inhalt, die unsere <section> enthält, und eine auto-Zeile für den Footer.

Jetzt positionieren wir unseren <footer> innerhalb dieses Grids, anstatt direkt in #app.

#app > footer {
  grid-area: 2 / 1 / 3 / 4;
}

Da <main> das Element ist, das scrollt, und da dieses Element nun unseren Footer enthält, haben wir den gewünschten Sticky-Footer erreicht! Auf diese Weise, wenn <main> Inhalt hat, der den Viewport überschreitet, scrollt das Ganze, und dieser scrollende Inhalt wird nun unseren Footer beinhalten, der sich wie erwartet ganz unten auf dem Bildschirm befindet.

Hier ist eine aktualisierte Demo. Beachten Sie, dass der Footer, wenn möglich, am unteren Bildschirmrand platziert wird; andernfalls scrollt er nach Bedarf. 

Ich habe einige andere kleine Änderungen vorgenommen, wie z. B. geringfügige Anpassungen der Abstände hier und da; wir dürfen keine linken oder rechten Abstände bei <main> haben, da der <footer> sonst nicht mehr von Rand zu Rand gehen würde.

Ich habe auch eine letzte Anpassung bei den Endbearbeitungen am <section>-Element vorgenommen – dem, bei dem wir den Inhalt mit verstellbarer Breite aktiviert haben. Speziell habe ich seine Anzeige auf flex, seine Breite auf 100 % und seinen direkten Nachfahren auf overflow: auto gesetzt. Dies habe ich getan, damit der Inhalt des <section>-Elements horizontal, innerhalb sich selbst, scrollen kann, wenn er unsere Grid-Spaltengrenze überschreitet, aber ohne vertikales Scrollen zu erlauben.

Ohne diese Änderung würde die Arbeit, die wir geleistet haben, der festen Footer-Methode entsprechen, die wir zuvor behandelt haben. Wenn man section> zu einem Flex-Container macht, wird sein direkter Nachfahre – die <div>, die den Inhalt enthält – den gesamten verfügbaren vertikalen Platz einnehmen. Und natürlich ermöglicht das Setzen dieser Child-Div auf overflow: auto das Scrollen. Wenn Sie sich fragen, warum ich nicht einfach overflow-x des Sektions auf auto und overflow-y auf visible gesetzt habe, nun, es stellt sich heraus, dass das nicht möglich ist.

Abschließende Gedanken

Wir haben in diesem Beitrag nichts Revolutionäres getan, und sicherlich nichts, das vor CSS Grid nicht hätte erreicht werden können. Unser <main>-Container mit fester Breite hätte ein Blockelement mit einem max-width-Wert von 600px und automatischen Rändern links und rechts sein können. Unser fester Footer hätte mit position: fixed erstellt werden können (stellen Sie nur sicher, dass der Hauptinhalt ihn nicht überlappt). Und natürlich gibt es verschiedene Möglichkeiten, einen traditionelleren "Sticky-Footer" zu erzielen.

Aber CSS Grid bietet einen einzigen, einheitlichen Layout-Mechanismus, um all dies zu erreichen, und es macht Spaß damit zu arbeiten – ehrlich gesagt Spaß. Tatsächlich war die Idee, den Footer von fest zu sticky zu ändern, nicht einmal etwas, das ich anfangs geplant hatte. Ich habe es in letzter Minute hinzugefügt, weil ich dachte, der Beitrag wäre ohne etwas zu leicht. Es war trivial zu erreichen, im Grunde das Verschieben von Grid-Zeilen, nicht unähnlich dem Zusammenfügen von Legosteinen. Und wieder, diese UIs waren trivial. Stellen Sie sich vor, wie hell Grid bei ehrgeizigeren Designs glänzen wird!