Как 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;
Что нужно запомнить
Flexbox работает с разницей между доступным пространством контейнера и размерами его элементов.
Если места больше, чем нужно элементам, появляется свободное пространство.
flex-growпозволяет элементам забрать свободное пространство и увеличиться.Если места недостаточно, возникает дефицит пространства.
flex-shrinkопределяет, могут ли элементы уменьшаться.flex-basisзадает исходный размер элемента вдоль главной оси.flexобъединяетflex-grow,flex-shrinkиflex-basis.margin: autoтоже может использовать свободное пространство, но это отдельный механизм.