Wypełnij pozostałą przestrzeń pionową CSS za pomocą display: flex

196

W układzie 3-rzędowym:

  • górny rząd powinien być dopasowany do jego zawartości
  • dolny rząd powinien mieć stałą wysokość w pikselach
  • środkowy rząd powinien się rozwinąć, aby wypełnić pojemnik

Problem polega na tym, że wraz z rozszerzaniem się głównej treści powoduje ona skrócenie wierszy nagłówka i stopki:

Flexing Bad

HTML:

<section>
  <header>
    header: sized to content
    <br>(but is it really?)
  </header>
  <div>
    main content: fills remaining space<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- uncomment to see it break - ->
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- -->
  </div>
  <footer>
    footer: fixed height in px
  </footer>
</section>

CSS:

section {
  display: flex;
  flex-flow: column;
  align-items: stretch;
  height: 300px;
}
header {
  flex: 0 1 auto;
  background: tomato;
}
div {
  flex: 1 1 auto;
  background: gold;
  overflow: auto;
}
footer {
  flex: 0 1 60px;
  background: lightgreen;
  /* fixes the footer: min-height: 60px; */
}

Skrzypce:

Mam szczęście, że mogę korzystać z najnowszych i najlepszych w CSS, bez względu na starsze przeglądarki. Pomyślałem, że mogę użyć układu Flex, aby w końcu pozbyć się starych układów opartych na tabelach. Z jakiegoś powodu nie robi tego, co chcę ...

Dla przypomnienia, istnieje wiele powiązanych pytań na temat SO dotyczących „wypełnienia pozostałej wysokości”, ale nic, co nie rozwiązałoby problemu z flexem. Refs:

Zilk
źródło
Wydaje się, że działa na skrzypcach zgodnie z oczekiwaniami.
Dayan
Tak, musisz odkomentować resztę zawartości <div>, aby zobaczyć, jak się psuje. Może powinienem był powiązać zepsutą wersję. Przepraszam.
Zilk,
Dodałem teraz obie wersje do pytania.
Zilk,
Rozumiem teraz, co masz na myśli.
Dayan
dup of stackoverflow.com/questions/90178/... ?
Jonathan Stray

Odpowiedzi:

246

Uprość : DEMO

section {
  display: flex;
  flex-flow: column;
  height: 300px;
}

header {
  background: tomato;
  /* no flex rules, it will grow */
}

div {
  flex: 1;  /* 1 and it will fill whole space left if no flex value are set to other children*/
  background: gold;
  overflow: auto;
}

footer {
  background: lightgreen;
  min-height: 60px;  /* min-height has its purpose :) , unless you meant height*/
}
<section>
  <header>
    header: sized to content
    <br/>(but is it really?)
  </header>
  <div>
    main content: fills remaining space<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- uncomment to see it break -->
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- -->
  </div>
  <footer>
    footer: fixed height in px
  </footer>
</section>

Wersja pełnoekranowa

section {
  display: flex;
  flex-flow: column;
  height: 100vh;
}

header {
  background: tomato;
  /* no flex rules, it will grow */
}

div {
  flex: 1;
  /* 1 and it will fill whole space left if no flex value are set to other children*/
  background: gold;
  overflow: auto;
}

footer {
  background: lightgreen;
  min-height: 60px;
  /* min-height has its purpose :) , unless you meant height*/
}

body {
  margin: 0;
}
<section>
  <header>
    header: sized to content
    <br/>(but is it really?)
  </header>
  <div>
    main content: fills remaining space<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- uncomment to see it break -->
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br> x
    <br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    <!-- -->
  </div>
  <footer>
    footer: fixed height in px
  </footer>
</section>

Cyrylica
źródło
20
Co jeśli chcemy, aby sekcja miała 100% wysokości?
Paul Totzke,
5
@PaulTotzke To bardziej przypomina inne pytanie, wystarczy ustawić wysokość na 100%. Jak zwykle, rodzice potrzebują zestawu wysokości / użytecznego, w przeciwnym razie mamy klasyczny przykład 100% wartości null dla powyższego kodu: html, body, section {height: 100%;} gdzie sekcja jest bezpośrednim dzieckiem potomka jsfiddle.net/ 7yLFL / 445 daje to stały nagłówek i stopkę.
G-Cyrillus
1
@GCyrillus Dziękujemy. Nie zdawałem sobie sprawy, że HTML, ciało ORAZ opakowanie musiało mieć ustawiony rozmiar.
Paul Totzke
1
Dzięki za „minimalna wysokość ma swój cel”. Zastanawiałem się, dlaczego to nie zadziałało, używając wysokości do ustalonych elementów.
Maciej Krawczyk
2
Wystąpił problem z tym rozwiązaniem w Firefoksie. Naprawiłem to, zmieniając flex: 1na flex-grow: 1. Dzięki za rozwiązanie!
strange_developer
19

Poniższy przykład obejmuje przewijanie, jeśli zawartość rozwiniętego komponentu środkowego wykracza poza jego granice. Również komponent środkowy zajmuje 100% pozostałego miejsca w rzutni.

jsfiddle tutaj

html, body, .r_flex_container{
    height: 100%;
    display: flex;
    flex-direction: column;
    background: red;
    margin: 0;
}
.r_flex_container {
    display:flex;
    flex-flow: column nowrap;
    background-color:blue;
}

.r_flex_fixed_child {
    flex:none;
    background-color:black;
    color:white;

}
.r_flex_expand_child {
    flex:auto;
    background-color:yellow;
    overflow-y:scroll;
}

Przykład kodu HTML, którego można użyć do zademonstrowania tego zachowania

<html>
<body>
    <div class="r_flex_container">
      <div class="r_flex_fixed_child">
        <p> This is the fixed 'header' child of the flex container </p>
      </div>
      <div class="r_flex_expand_child">
            <article>this child container expands to use all of the space given to it -  but could be shared with other expanding childs in which case they would get equal space after the fixed container space is allocated. 
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,
            </article>
      </div>
      <div class="r_flex_fixed_child">
        this is the fixed footer child of the flex container
        asdfadsf
        <p> another line</p>
      </div>

    </div>
</body>
</html>
gamozzii
źródło
3
Można to nieco uprościć, usuwając htmlz selektora i dostosowując height: 100vhdo body: jsfiddle.net/pm6nqcqh/1
Dai
Zauważyłem różnicę między flex: auto a flex: 1 na rozwijanym dziecku. Z taśmą: auto, inne dzieci zdają się kurczyć, gdy są potrzebne, z taśmą: 1 tak nie jest (w Chrome)
mvermand
7

Bardziej nowoczesnym podejściem byłoby użycie właściwości grid.

section {
  display: grid;
  align-items: stretch;
  height: 300px;
  grid-template-rows: min-content auto 60px;
}
header {
  background: tomato;
}
div {
  background: gold;
  overflow: auto;
}
footer {
  background: lightgreen;
}
<section>
  <header>
    header: sized to content
    <br>(but is it really?)
  </header>
  <div>
    main content: fills remaining space<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    
  </div>
  <footer>
    footer: fixed height in px
  </footer>
</section>

wuppie367
źródło
1
Obsługa siatki CSS jest dość nowa. Nie użyłbym tego, chyba że nie będziesz musiał obsługiwać starych przeglądarek.
adi518,
4

Użyj flex-growwłaściwości do div div głównej treści i przekaż dispaly: flex;jej rodzicowi;

body {
    height: 100%;
    position: absolute;
    margin: 0;
}
section {
  height: 100%;
  display: flex;
  flex-direction : column;
}
header {
  background: tomato;
}
div {
  flex: 1; /* or flex-grow: 1  */;
  overflow-x: auto;
  background: gold;
}
footer {
  background: lightgreen;
  min-height: 60px;
}
<section>
  <header>
    header: sized to content
    <br>(but is it really?)
  </header>
  <div>
    main content: fills remaining space<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
    x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
  </div>
  <footer>
    footer: fixed height in px
  </footer>
</section>

Deepu Reghunath
źródło
Czy istnieje inne rozwiązanie tego problemu (gdzie sectionwysokość nie jest ustalona?) Zamiast używania position: absolute?
Ben
1
@ben Jeśli znasz wysokość elementu. Wtedy możesz uniknąć używania position: absolute;. https://jsfiddle.net/xa26brzf/
Deepu Reghunath