Задача


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

image


Реализация


При реализации данной задачи было принято решение использовать CSS Flexbox.
Для начала следует решить задачу без учета контента, т.е. с двумя пустыми дочерними блоками:

image

Как это работает


Родительскому блоку (на рисунке обозначен зеленым цветом) задан display: flex; и flex-direction: column; для задания направления контента. Дочерним блокам заданы стили flex: 0 0 auto; для блока, для которого известна высота, и flex: 1 0 auto; для блока, который бодет растягиваться на все свободное место. «1» для второго блока в данном случае означает «жадность», т.е. более жадный блок занимает больше места, чем менее жадный, а т.к. в данном случае у менее жадного задана высота (может быть задана максимальная высота), то более жадный блок занимает все свободное место.

Добавим контент в нижний блок


Для добавления скролла тут достаточно указать overflow: auto;

image

Добавим контент в верхний блок


Тут так просто, как с нижним блоком, не выйдет. Все потому, что flex учитывает ее и высоту контента.

image

Чтобы решить эту проблему, контент необходимо спозиционировать абсолютно (position: absolute;) и задать ему свойства top, right, bottom, left равными нулю (чтобы растянуть его по размерам блока)

image

Теперь блок занимает нужный нам размер, однако контент вылазит из него. Для решения этой проблемы необходимо родительскому для контента блоку задать position: relative; overflow: auto;

image

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

image

Результат (HTML):
<div class="container">
  <div class="first-child">
    <div class="first-content">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
    </div>    
  </div>
  <div class="second-child">
    <div class="second-content">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
    </div>
  </div>
</div>


Результат (SCSS)
.container {
  display: flex;
  flex-direction: column;
  width: 150px;
  height: 200px;
  background-color: #A5D6A7;
  
  .first-child {    
    flex: 1 0 auto;
    position: relative;
    overflow: auto;
    width: 125px;
    box-sizing: border-box;
    background-color: #90CAF9;
    
    .first-content {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      padding: 5px;
    }
  }
  
  .second-child {
    flex: 0 0 auto;
    width: 125px;
    max-height: 75px;
    padding: 5px;
    box-sizing: border-box;
    overflow: auto;
    background-color: #CE93D8;
  }
}


P.S. Работающий пример можно посмотреть тут
Поделиться с друзьями
-->

Комментарии (14)


  1. MooNDeaR
    14.10.2016 08:47

    CSS — это просто, а главное — интуитивно понятно!


  1. ScorpAL
    14.10.2016 10:43
    +2

    Не хватает заголовка "Где это работает".

    Почему все так стесняются протестировать в нескольких браузерах и честно написать что работает в таком и таком браузере, но не работает в таком и таком?


    1. DarthVictor
      14.10.2016 12:18

      Давайте поможем автору в этом.
      Работает в:

      • Chrome 53.0.2785.143
      • IE 11.321.14393.0
      • Microsoft EdgeHTML 14.14393, Microsoft Edge 38.14393.0.0
      • Firefox 43.0.1
      • Эмулятор Хрома для мобилок


      1. EvgenyMakhnovets
        14.10.2016 12:40

        Спасибо, забыл об этом в статье.
        P.S. если использовать префиксы, то работает и в IE 10. Ну то есть все современные браузеры поддерживаются


  1. shlangus
    14.10.2016 12:38

    Я не уверен что правильно понял описание задачи, но вот это похоже на то, что вы сделали, только без 2 лишних блоков.


    1. EvgenyMakhnovets
      14.10.2016 12:39

      В данном случае, если уменьшить контент в нижнем блоке он автоматически не уменьшится по величине


      1. shlangus
        14.10.2016 14:11
        +1

        Этот случай вы изобразили на правой картинке? Из-за элипсиса я не сообразил к чему она. Позвольте ещё одну попытку.


  1. ysegorov
    14.10.2016 12:39

    чуть проще можно сделать


    1. EvgenyMakhnovets
      14.10.2016 12:39

      Та же проблема, что и в предыдущем комментарии


  1. Serator
    14.10.2016 13:13
    +1

    @EvgenyMakhnovets Уже много раз писал (не конкретно вам, а вообще), но толку все равно нет. Забудьте о свойстве flex. Пишите все раздельно. Тогда и про "растягивается", "жадность" и прочие недоразумения писать не нужно будет, так как об этом скажет ваш код, а не набор непонятных 0 0 auto и т.п.


    И вот, кстати, решение задачи из статьи, только без горы мусора и с читаемым CSS'ом: https://jsbin.com/gajozaj/5/edit?html,css,output.


  1. DenimTornado
    14.10.2016 15:46

    А можете рассказать чуть больше про изначальную задачу? Честно говоря какое-то странное поведение. И если честно, не до конца понятно, что подразумевается под «максимальной» высотой? То есть первый блок должен динамически расширяться до определённого момента, так? В общем, хотелось бы чуть подробнее о задаче узнать. Заране спасибо!


    1. EvgenyMakhnovets
      14.10.2016 20:24

      Нижних блок имеет максимальную высоту, и его высота равна контенту, если меньше максимальной (в противном случае = максимальной + скрол). Верхний — растягивается на все оставшееся место в контернере (по высоте), если не влазит, то добавляется скрол


  1. pepelsbey
    16.10.2016 23:20

    Вы же понимаете, что у вас в CSS будет чудовищный селектор .container .first-child .first-content? Не нужна это вложенность здесь, да нигде она не нужна. Думайте иногда о том, что выдаёт вам Sass.


    1. EvgenyMakhnovets
      18.10.2016 11:51

      Да, косяк мой, но статья не совсем об этом)