Как Flexbox распределяет свободное пространство

Flexbox умеет не только располагать элементы в определенном направлении. Он также может управлять свободным пространством внутри контейнера.

Например, элементы могут:

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

Откуда берется свободное пространство

Рассмотрим простой контейнер:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>

Пусть контейнер имеет ширину 800px, а каждый элемент — 150px:

.container {
  display: flex;
  width: 800px;
}

.item {
  width: 150px;
}

Три элемента занимают 150 + 150 + 150 = 450px, значит свободное пространство — 350px (800 — 450).

Именно с этим пространством Flexbox может работать.

Flex-grow

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

По умолчанию:

flex-grow: 0;

Это означает, что элементы не увеличиваются автоматически только потому, что в контейнере осталось свободное место. Если к прошлому примеру, где осталось 350px, дописать flex-grow: 1, элемент получает право увеличиться и занять часть свободного пространства.

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

Например:

.item:nth-child(1) {
  flex-grow: 1;
}

.item:nth-child(2) {
  flex-grow: 1;
}

.item:nth-child(3) {
  flex-grow: 1;
}

У всех трех элементов одинаковый коэффициент, поэтому свободное пространство будет распределено между ними поровну.

Но коэффициенты могут отличаться:

.item:nth-child(1) {
  flex-grow: 1;
}

.item:nth-child(2) {
  flex-grow: 2;
}

.item:nth-child(3) {
  flex-grow: 1;
}

Теперь второй элемент получает в два раза большую долю свободного пространства, чем первый и третий.

Всего коэффициентов:

1 + 2 + 1 = 4

Поэтому свободное пространство делится на четыре условные части:

  • первый элемент получает одну часть;
  • второй — две;
  • третий — одну.

Flex-shrink

До этого мы рассматривали ситуацию, когда места больше, чем нужно элементам. Но возможна и обратная ситуация.

Например:

.container {
  display: flex;
  width: 600px;
}

.item {
  width: 250px;
}

Три элемента хотят занять:

250 + 250 + 250 = 750px

Но контейнер имеет только 600px, свободного пространства больше нет.

Наоборот, появился недостаток места:

600 - 750 = -150px

Теперь Flexbox должен решить, что делать с этой разницей. За это отвечает flex-shrink.

Свойство flex-shrink определяет, насколько flex-элемент может уменьшаться, если для всех элементов не хватает места.

По умолчанию flex-shrink: 1, то есть flex-элементам разрешено уменьшаться. В нашем случае, если стоит значение по умолчанию, элементы могут уменьшиться даже несмотря на заданную им ширину, чтобы поместиться в контейнер.

Однако, если задать элементу flex-shrink: 0, то он перестанет сжиматься. В результате их суммарный размер окажется больше доступного пространства контейнера, и содержимое может выйти за его пределы.

Как и flex-grow, flex-shrink принимает числовой коэффициент.

Flex-basis

До сих пор в примерах мы задавали исходный размер элементов через width:

.item {
  width: 200px;
}

У flex-элементов есть специальное свойство flex-basis.

Оно определяет исходный размер flex-элемента вдоль главной оси до распределения свободного или недостаточного пространства.

Например:

.item {
  flex-basis: 200px;
}

При стандартном flex-direction: row, flex-basis определяет исходную ширину элемента.

При:

flex-direction: column;

он определяет исходную высоту.

Flex

Для flex-grow, flex-shrink и flex-basis существует сокращенная запись:

flex: 1;

Это очень распространенная запись во Flexbox.

Например:

.container {
  display: flex;
}

.item {
  flex: 1;
}

Так часто делают, когда несколько элементов должны равномерно разделить доступное пространство.

Также можно явно указать все три значения:

.item {
  flex: 1 1 200px;
}

Здесь:

1      → flex-grow
1      → flex-shrink
200px  → flex-basis

Как все эти свойства работают вместе

Теперь соберем картину целиком.

Предположим:

.container {
  display: flex;
}

А у элементов:

.item {
  flex: 1 1 200px;
}

Это означает:

  • flex-basis: 200px — сначала каждый элемент стремится получить базовый размер 200px;
  • flex-grow: 1 — если свободного места больше, элементы могут увеличиваться;
  • flex-shrink: 1 — если места меньше, элементы могут уменьшаться.

Таким образом, Flexbox сначала определяет базовые размеры элементов, затем смотрит, сколько места осталось или не хватает, и после этого распределяет эту разницу.

Если места больше:

свободное пространство → flex-grow

Если места меньше:

недостаток пространства → flex-shrink

Margin: auto и свободное пространство

Есть еще один интересный способ использовать свободное пространство.

Во flex-контейнере автоматический внешний отступ:

margin-left: auto;

может забрать все доступное свободное пространство с соответствующей стороны.

Например:

<div class="toolbar">
  <div class="logo">Logo</div>
  <nav class="menu">
    <a href="#">Главная</a>
    <a href="#">Каталог</a>
  </nav>
</div>

Можно написать:

.toolbar {
  display: flex;
}

.menu {
  margin-left: auto;
}

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

Это очень распространенный прием, особенно когда нужно прижать только один конкретный элемент к противоположному краю.

Как выбрать подходящий инструмент

На практике можно ориентироваться на задачу.

Если нужно, чтобы сами элементы забрали свободное пространство и увеличились, используйте:

flex-grow

Если места не хватает и элементы должны иметь возможность уменьшаться, используется:

flex-shrink

Если нужно определить исходный размер элемента вдоль главной оси, используется:

flex-basis

Если нужно прижать конкретный элемент к противоположному краю, часто удобно использовать:

margin-left: auto;

Что нужно запомнить

  1. Flexbox работает с разницей между доступным пространством контейнера и размерами его элементов.

  2. Если места больше, чем нужно элементам, появляется свободное пространство.

  3. flex-grow позволяет элементам забрать свободное пространство и увеличиться.

  4. Если места недостаточно, возникает дефицит пространства.

  5. flex-shrink определяет, могут ли элементы уменьшаться.

  6. flex-basis задает исходный размер элемента вдоль главной оси.

  7. flex объединяет flex-grow, flex-shrink и flex-basis.

  8. margin: auto тоже может использовать свободное пространство, но это отдельный механизм.

предыдущий пост следующий пост