Как реально работают flex-shrink и flex-grow

Механизм расчёта свободного места во флекс-контейнере при помощи свойств flex-grow и flex-shrink.

Время чтения: 9 мин

flex-grow и flex-shrink — это CSS-свойства, определяющие, какое количество свободного пространства флекс-контейнера может занимать текущий элемент.

Рассмотрим каждое свойство в отдельности.

flex-grow

Скопировано

Создадим флекс-контейнер с двумя вложенными элемента с разными значениями flex-grow.

        
          
          <div class="container">  <code class="container-item-grown">flex-grow: 2;</code>  <code class="container-item-common">flex-grow: 1;</code></div>
          <div class="container">
  <code class="container-item-grown">flex-grow: 2;</code>
  <code class="container-item-common">flex-grow: 1;</code>
</div>

        
        
          
        
      
        
          
          .container {  display: flex;  width: 1200px;  height: 100px;}.container-item-grown {  background-color: #2E9AFF;  flex-grow: 2;}.container-item-common {  background-color: #F498AD;  flex-grow: 1;}
          .container {
  display: flex;
  width: 1200px;
  height: 100px;
}

.container-item-grown {
  background-color: #2E9AFF;
  flex-grow: 2;
}

.container-item-common {
  background-color: #F498AD;
  flex-grow: 1;
}

        
        
          
        
      
Открыть демо в новой вкладке

У нас есть контейнер шириной 1200 px, для первого элемента указан flex-grow: 2, а для второго — flex-grow: 1. Первый элемент занимает 800 px, а второй 400 px.

Хочется думать, что здесь всё просто и работает по правилам математики:

  • сложить значения свойства для каждого контейнера и получить 3 (2 + 1);
  • поделить ширину контейнера на общее количество «частей» и получить 400 px (1200 px / 3);
  • для каждого блока использовать значение flex-grow как вес и умножить на ширину одной части;
  • для первого блока 400 px * 2 = 800 px;
  • для второго блока 400 px * 1 = 400 px.

Пока всё верно. Однако, часто на практике мы ожидаем, что сейчас мы укажем flex-grow и это нас спасёт и… не работает.

Вот пример, такого «не работает».

        
          
          <div class="container">  <div class="container-item-grown">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div>  <div class="container-item-common">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div></div>
          <div class="container">
  <div class="container-item-grown">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
  <div class="container-item-common">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
</div>

        
        
          
        
      
        
          
          .container {  display: flex;  width: 1200px;}.container-item-grown {  background-color: #2E9AFF;  flex-grow: 2;}.container-item-common {  background-color: #F498AD;  flex-grow: 1;}
          .container {
  display: flex;
  width: 1200px;
}

.container-item-grown {
  background-color: #2E9AFF;
  flex-grow: 2;
}

.container-item-common {
  background-color: #F498AD;
  flex-grow: 1;
}

        
        
          
        
      
Открыть демо в новой вкладке

Пример отличается от предыдущего тем, что в блоках появился текст, обёрнутый в параграф. И результат таков, что блоки стали одинаковыми по ширине.

Получается, что логика рассуждений, приведённая выше, не работает?
Не совсем так, давайте разбираться.

Деталь кроется в том, что flex-grow определяет, какое количество свободного места занимает элемент. И если внимательно посмотреть, то каждый блок текста занял по 600 px ширины, а свободного пространства не осталось. Поэтому и распределять нечего.

Почему же в первом примере всё работает ожидаемо? Там логика сработала, потому что для блоков не указана ширина и в них нет вложенных элементов, а значит ширина обоих 0 px, и свободное место — это и есть ширина контейнера, то есть 1200 px.

И что, если свободного места нет, то использовать flex-grow не получится? Не совсем, нужно просто добавить свойство flex-basis. Если использовать эти свойства в комплекте, то алгоритм расчёта ширины изменится.

flex-basis — это свойство, которое устанавливает начальный основной размер флекс-элемента. Его можно задать в любых единицах, в которых можно задать ширину элемента.

В результате мы получим то, чего хотим.

        
          
          <div class="container">  <div class="container-item-grown">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div>  <div class="container-item-common">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div></div>
          <div class="container">
  <div class="container-item-grown">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
  <div class="container-item-common">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
</div>

        
        
          
        
      
        
          
          .container {  display: flex;  width: 1200px;}.container-item-grown {  background-color: #2E9AFF;  flex-grow: 2;  flex-basis: 800px;}.container-item-common {  background-color: #F498AD;  flex-grow: 1;  flex-basis: 400px;}
          .container {
  display: flex;
  width: 1200px;
}

.container-item-grown {
  background-color: #2E9AFF;
  flex-grow: 2;
  flex-basis: 800px;
}

.container-item-common {
  background-color: #F498AD;
  flex-grow: 1;
  flex-basis: 400px;
}

        
        
          
        
      
Открыть демо в новой вкладке

Ещё один пример без flex-basis для закрепления. Попробуем задать ширину параграфов: для первого параграфа зададим 300 px, а для второго 600 px.

        
          
          <div class="container">  <div class="container-item-grown">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div>  <div class="container-item-common">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div></div>
          <div class="container">
  <div class="container-item-grown">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
  <div class="container-item-common">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
</div>

        
        
          
        
      
        
          
          .container {  display: flex;  width: 1200px;}.container-item-grown {  background-color: #2E9AFF;  flex-grow: 2;}.container-item-common {  background-color: #F498AD;  flex-grow: 1;}.container-item-grown p {  width: 300px;}.container-item-common p {  width: 600px;}
          .container {
  display: flex;
  width: 1200px;
}

.container-item-grown {
  background-color: #2E9AFF;
  flex-grow: 2;
}

.container-item-common {
  background-color: #F498AD;
  flex-grow: 1;
}

.container-item-grown p {
  width: 300px;
}

.container-item-common p {
  width: 600px;
}

        
        
          
        
      
Открыть демо в новой вкладке

Здесь получается не совсем ожидаемый от flex-grow результат, если не знать как работает свойство на самом деле. Но мы-то уже знаем, давайте посчитаем:

  • Первый блок занимает 300 px. Это свойство задано для селектора p.
  • Второй блок занимает 600 px. Причина всё та же — так задано в дочернем элементе.
  • Итого, для распределения свойством flex-grow остаётся 300 px. Далее применим логику, которая была описана чуть выше, и…
  • На первый элемент придётся 200 px, а на второй — всего 100 px.
  • В итоге первый элемент займёт 500 px (300 px + 200 px), а второй - 700 px (600 px + 100 px).

Что ещё здесь важно помнить?

Скопировано

Как работает свойство box-sizing. Вычисление свойства flex-basis также, как и свойства width зависит от box-sizing, и результат, в зависимости от указанного значения, будет разным.

flex-shrink

Скопировано

Теперь посмотрим на свойство flex-shrink. Его мы используем в тех случаях, когда места не хватает и размеры элементов флекс-контейнера нужно уменьшить. Здесь математика чуть сложнее, вот пример:

        
          
          <div class="container">  <div class="container-item-shrikned">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div>  <div class="container-item-common">    <p>      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.    </p>  </div></div>
          <div class="container">
  <div class="container-item-shrikned">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
  <div class="container-item-common">
    <p>
      Равным образом сложившаяся структура организации требуют определения и уточнения модели развития. Повседневная практика показывает, что дальнейшее развитие различных форм деятельности обеспечивает широкому кругу (специалистов) участие в формировании систем массового участия.
    </p>
  </div>
</div>

        
        
          
        
      
        
          
          .container {  display: flex;  width: 800px;}.container-item-shrinked {  background-color: #2E9AFF;  flex-shrink: 2;  flex-basis: 400px;}.container-item-common {  background-color: #F498AD;  flex-shrink: 1;  flex-basis: 800px;}
          .container {
  display: flex;
  width: 800px;
}

.container-item-shrinked {
  background-color: #2E9AFF;
  flex-shrink: 2;
  flex-basis: 400px;
}

.container-item-common {
  background-color: #F498AD;
  flex-shrink: 1;
  flex-basis: 800px;
}

        
        
          
        
      
Открыть демо в новой вкладке

У нас в примере два элемента, у каждого есть изначальная ширина (flex-basis равен 400 px и 800 px соответственно). Итого 1200 px. Однако ширина флекс-контейнера всего 800 px.

Сначала посчитаем общий вес при том, что для первого элемента значение flex-shrink составляет 2, а для второго — 1: 2 * 400 px + 1 * 800 px = 1600 px.
Для полноценного размещения нам не хватает 1200 px - 800 px = 400 px.

Подсчёт сокращения будет выглядеть следующим образом:

  • Пространство, которого не хватает, умножим на значение flex-shrink.
  • Затем результат умножим на значение flex-basis.
  • Результат предыдущего действия разделим на общий вес.

Считаем:

  • 400 px * 2 * 400 px / 1600 px = 200 px
  • 400 px * 1 * 800 px / 1600 px = 200 px

А в результате ширина блоков будет следующая:

  • 400 px - 200 px = 200 px
  • 800 px - 200 px = 600 px

Вывод: с помощью свойств flex-basis, flex-grow, flex-shrink можно предусмотреть как ситуации, когда контейнер окажется больше, чем элементы, в нём размещённые, так и наоборот - когда контейнер мал для элементов. А если знать как рассчитываются итоговые размеры, то можно избежать различных неожиданностей.