Заполните оставшееся вертикальное пространство с помощью CSS, используя display: flex

196

В 3-рядном макете:

  • верхний ряд должен быть измерен согласно его содержанию
  • нижний ряд должен иметь фиксированную высоту в пикселях
  • средний ряд должен расширяться, чтобы заполнить контейнер

Проблема заключается в том, что при расширении основного содержимого он разделяет строки верхнего и нижнего колонтитула:

Изгибать плохо

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; */
}

Скрипки:

Мне повезло, что я могу использовать последние и лучшие в CSS, не считая устаревших браузеров. Я думал, что смогу использовать гибкий макет, чтобы наконец избавиться от старых макетов на основе таблиц. По какой-то причине он не делает то, что я хочу ...

Напомним, что в SO есть много вопросов о «заполнении оставшейся высоты», но нет ничего, что решало бы проблему, с которой я столкнулся с flex. Refs:

Zilk
источник
Кажется, работает как положено на скрипке.
Даян
Да, вам нужно раскомментировать оставшуюся часть содержимого <div>, чтобы увидеть, как оно ломается. Возможно я должен был связать сломанную версию. Сожалею.
Zilk
Я добавил обе версии к вопросу сейчас.
Zilk
Теперь я понимаю, что вы имеете в виду.
Даян
dup of stackoverflow.com/questions/90178/… ?
Джонатан

Ответы:

246

Сделай это просто: 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>

Полноэкранная версия

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>

G-Кирилл
источник
20
Что если мы хотим, чтобы секция имела 100% высоту?
Пол Тоцке
5
@PaulTotzke Тогда это больше похоже на другой вопрос, вам просто нужно установить высоту на 100%. Как обычно, родители нуждаются в наборе высоты / пригодном для использования, иначе у нас есть классический пример «нулевой» 100% для кода выше: html, body, section {height: 100%;}, где section является прямым потомком body jsfiddle.net/ 7yLFL / 445 это дает фиксированный верхний и нижний колонтитулы.
G-Cyrillus
1
@GCyrillus Спасибо. Я не понимал, HTML, тело и оболочка должны были иметь установленный размер.
Пол Тоцке,
1
Спасибо за "min-height имеет свое назначение". Мне было интересно, почему это не сработало, используя высоту для фиксированных элементов.
Мацей Кравчик
2
Была проблема с этим решением в Firefox. Я исправил это, изменив flex: 1на flex-grow: 1. Спасибо за решение!
странный_разработчик
19

Пример ниже включает в себя поведение прокрутки, если содержимое расширенного центрального компонента выходит за его границы. Также центральный компонент занимает 100% оставшегося пространства в области просмотра.

jsfiddle здесь

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;
}

Пример HTML, который можно использовать для демонстрации такого поведения

<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
источник
3
Это можно немного упростить, удалив htmlиз селектора и применив height: 100vhк нему body: jsfiddle.net/pm6nqcqh/1
Дай
Я заметил разницу между flex: auto и flex: 1 для расширяемого потомка. При использовании flex: auto другие дети, по мере необходимости, сжимаются, при использовании flex: 1 они этого не делают (в Chrome)
mvermand
7

Более современный подход заключается в использовании свойства 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
источник
1
Поддержка CSS-сетки довольно нова. Я бы не использовал его, если вы не должны поддерживать старые браузеры.
adi518
4

Используйте flex-growсвойство для основного содержимого div и передайте dispaly: flex;его родителю;

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>

Дипу Регунат
источник
Есть ли другое решение этой проблемы (где sectionвысота не фиксирована?), А не использовать position: absolute?
Бен
1
@ben Если вы знаете высоту элемента. Тогда вы можете избежать использования position: absolute;. https://jsfiddle.net/xa26brzf/
Дипу Регунат