Флексбоксы ч.1
Обновлено
06.11.2023Просмотров
289Время прочтения
40 мин.Сложность
Flexbox модель, или на сленге «флексы»,- это последнее, что вы должны знать для того, чтобы начать полноценно верстать веб страницы. Конечно, в CSS ещё очень много различных тем, свойств, возможностей для изучения, но фундамент всей вашей работы — это блочная модель, позиционирование и flexbox. Знания этих разделов достаточно, чтобы создать практически любую структуру сайта, с хэдэром, футером, сайдбаром, с навигацией, карточками продуктов, модальными окнами и прочим. Каким бы сложным не выглядел элемент на сайте, если присмотреться, он окажется обыкновенным тегом, обладающим определённым display, position, width и height. Для полного понимания происходящего вам не хватает только флексов, и поэтому сейчас мы приступим к изучению этой важнейшей темы.
Flexbox — что это
Это модель отображения элементов в CSS3, позволяющая весьма просто располагать элементы нужным нам образом. Эта модель состоит из двух компонентов — флекс контейнера и флекс элементов. Контейнером называется непосредственно родитель, к которому мы применяем эту модель, а флекс-элементами называются все его дети (не потомки!).
При помощи флексбоксов мы можем всего парой CSS свойств расположить элементы интерфейса в ряд в столбец, равномерно распределяя пространство между ними, или группируя их, выравнивая их по центру, слева, справа и пр. Конечно, мы могли бы сделать подобное и без флексов, но это было бы гораздо сложнее, а иногда и вовсе мучительно. Флексы, появившиеся в CSS3, облегчили работу разработчиков.
Итак, флексы состоят из флекс-контейнера и флекс-элементов. Для создания флекс-контейнера нам достаточно указать для тега display: flex. После этого элемент станет блочным, а все его дети станут флекс-элементами (не блочными и не строчными) и будут вести себя особенным образом. После указания display: flex как для контейнера, так и для детей будут доступны различные flex-свойства, отвечающие за выравнивание, размеры и положение флекс-элементов. Повторимся, сам флекс-контейнер становится блочным и более не приобретает никакого особенного поведения. Вся магия происходит с его детьми.
Прежде чем мы продолжим с флексами, следует вспомнить расположение осей координат на веб странице, т.к. это нам понадобится. Оси X и Y в браузере начинаются в верхнем левом углу. Также и оси по умолчанию для расположения флекс-элементов внутри флекс-контейнера начинаются в верхнем левом углу. В дальнейшем это поведение можно будет изменить.
Однако довольно теории, перейдём к практике. Следующий код нам в общем понятен, это один див-контейнер и находящиеся в нём три квадратных дива разных цветов. Разумеется, они блочные и, несмотря на указанный размер в 80px, маржей занимают всю ширину строки, а потому отображаются друг под другом:
<style>
.box{
width: 80px;
height: 80px;
}
.red{
background: red;
}
.green{
background: green;
}
.blue{
background: blue;
}
.container{
position: relative;
width: 300px;
background: #d3d3d3;
}
</style>
<div class="container">
<div class="box red"></div>
<div class="box blue"></div>
<div class="box green"></div>
</div>
Теперь добавим немного магических флексов, и увидим, как изменится отображение. Просто укажем следующий стиль для контейнера:
<style>
.container{
display: flex;
}
</style>
Как видите, наши квадраты выстроились в один ряд, и это совершенно логично с точки зрения модели flexbox. Как уже было сказано, данный стиль мы указываем для родителя, и его дочерние элементы становятся флекс-элементами. Они больше не являются как таковыми блочными или строчными, а ведут себя особенным образом. По умолчанию, при других отсутствующих флекс-свойствах, флекс-элементы выстраиваются по главной оси слева направо. Ширина флекс-элементов стремится к нулю. Так что, если бы мы не указывали ширину 80px, то элементы без контента сжались бы до ширины 0. Высота флекс-элементов становится равной высоте контент-части флекс-родителя. Однако в примере мы этого тоже не увидим, поскольку у родителя нет определённой высоты, она рассчитывается автоматически и наоборот исходит из высоты детей. Звучит довольно запутанно, но вы можете попробовать отключить свойство height для всех box и увидите, что все элементы, включая родителя, сжались до высоты 0.
Мы же для демонстрации базового определения высоты зададим для родителя фиксированную высоту в 150px, а также вертикальный padding в 10px. Для box мы уберём фиксированную высоту.
Теперь видно, что box растягиваются на всю возможную высоту родителя, их ограничивает только padding. Его, разумеется, они не могут занять. Такое поведение и является базовым для флекс-элементов без указанной высоты.
flex-direction
Это свойство применяется к флекс-контейнеру и определяет направление главной оси. Может принимать следующие значения:
row | значение по умолчанию. Главная ось начинается в левом верхнем углу флекс-контейнера |
row-reverse | главная ось начинается в правом верхнем углу флекс-контейнера |
column | Главная ось поворачивается на 90 градусов и теперь направлена сверху вниз. Поперечная ось, которая прежде отвечала за вертикальное выравнивание флекс-элементов, также поворачивается и теперь становится горизонтальной |
column-reverse | Главная ось поворачивается на 90 градусов и направлена снизу вверх. Поперечная ось также поворачивается и становится горизонтальной |
inherit | Наследует значение от родителя |
initial | принимает значение по умолчанию |
unset | значение не установлено. Поскольку свойство не является наследуемым, принимает значение row |
Теперь посмотрим на то, как эти свойства работают на практике, на нашем примере. При указании flex-direction: row-reverse ось начинается справа налево:
Стрелкой обозначено направление главной оси. Обратите внимание, что квадраты не просто расположились справа. Они изменили свой порядок согласно тому, как они расположены в разметке. Поскольку красный в разметке идёт первым, он и согласно оси тоже располагается первым. Отсюда можно понять, что это не просто выравнивание флекс-элементов слева\справа, а именно направление оси, по которой они будут расположены в родителе.
При указании flex-direction:column ось поворачивается на 90 градусов и направлена сверху вниз, при этом, разумеется, порядок элементов соответствующий:
Наконец, при указании flex-direction:column-reverse ось также поворачивается, но направлена уже снизу вверх:
Обратите внимание на порядок элементов. Красный первый!
Думаю, с главной осью стало немного понятнее.
flex-wrap
В нашем примере квадратов всего три, и они вполне умещаются в контейнер-родитель. Однако что произойдёт, если квадратов будет больше, и места всем не хватит?
Приведём сразу несколько примеров, чтобы всё разобрать подробно. В случае, когда квадратов просто станет больше и места им станет не хватать, они начнут сжиматься. Это и логично, если вспомнить, что с англ. flexible — гибкий:
Квадратов на картинке уже 6, каждый по 80px, в то время как контейнер остался шириной 300px. Отсюда можно сделать вывод, что они не могут поместиться в контейнер в той ширине, какая указана для них. И поэтому все квадраты сжимаются в равной степени. Итоговый размер каждого квадрата 300px / 6 = 50px. Думаю, это понятно, потому что это весьма логично.
Как уже было сказано, флекс элементы сжимаются до минимума, если для них не задана какая-либо ширина. В случае, если флекс элемент содержит какой-то контент, элемент сжимается так, чтобы только вместить этот контент. Отсюда можно сделать вывод, что если мы поместим какой-либо текст в наши боксы, они не смогут сжаться больше, чем это позволит текст. В подобном случае флекс элементы поведут себя следующим образом: все элементы сожмутся максимально, насколько это возможно, при этом будут учитываться их содержимое, указанный для них размер и некоторые другие flex-свойства, которые мы рассмотрим в дальнейшем.
Мы поместили небольшой текст в два квадрата, из-за этого они не могут сжаться так же, как другие. В результате мы получили два квадрата с разным размером, и четыре квадрата одинаковых размеров (они распределили свободное место между собой поровну). Если мы уберём текст из одного из квадратов, то пять квадратов без текста станут одного размера.
Ещё одна ситуация, которая заслуживает внимания, о которой и пойдёт речь в этой главе урока — недостаток места во флекс-контейнере. Если мы укажем для всех квадратов минимальную ширину 80px (до этого была указана обычная ширина width:80px), то места для всех не хватит, ведь сжаться квадраты меньше 80px уже не смогут.
Как видно на картинке, флекс элементы в таком случае просто выйдут за пределы контейнера. Что важно понимать — это всего лишь визуальное отображение, эти элементы не будут действительно занимать место на странице больше своего родителя, не подвинут никакие соседние элементы.
Этот ряд с элементами, которые не поместились, может быть сколь угодно длинным, вплоть до того, что он может выходить вообще за пределы страницы.
Такое поведение флекс-элементов является совершенно нормальным. Дело в том, что для определения подобного поведения есть специальное CSS свойство, flex-wrap. Именно оно определяет, могут ли флекс-элементы переноситься на следующую строку во флекс-контейнере.
nowrap | значение по умолчанию. Flex-элементы не переносятся на следующую строку |
wrap | флекс-элементы переносятся на следующую строку во флекс-контейнере |
wrap-reverse | флекс-элементы переносятся на следующую строку, при этом главная ось начинается с нижнего левого\правого угла контейнера |
inherit | Наследует значение от родителя |
initial | принимает значение по умолчанию |
unset | значение не установлено. Поскольку свойство не является наследуемым, принимает значение nowrap |
Мы добавили ещё несколько квадратов для наглядности и указали flex-wrap:wrap. Теперь, как видите, появилась многострочность: при недостатке места следующие элементы перенеслись на следующую строку. При этом, что важно, порядок flex-direction у нас сохранился, главная ось по прежнему начинается слева вверху.
Немного необычным выглядит значение wrap-reverse: оно переворачивает все элементы таким образом, что они начинают располагаться снизу вверх. Иными словами, главная ось теперь начинается слева\справа внизу:
Разумеется, значение flex-direction при подобных переносах учитывается: wrap-reverse при flex-direction: row-reverse (главная ось идёт справа налево и снизу вверх):
Что касается flex-direction: column, то, поскольку здесь для нас важна не ширина, а высота флекс-контейнера, понятия «строка» и «перенос строк» превращаются в колонки и перенос колонок. В остальном же это будет работать аналогичным образом (в примере мы для флекс-контейнера указали height: 300px):
Элементы сжались у нас таким же образом, как сжимались и при горизонтальном размещении. Что касается их длины, то эта длина теперь рассматривается как высота, а мы уже знаем из этого урока, что по умолчанию флекс-элементы растягиваются на всю высоту родителя.
При указании flex-wrap:wrap получаем следующий результат, что тоже вполне логично. Главная ось повернулась и теперь является вертикальной, все последующие строки ведут себя таким же образом:
Всё это может показаться сложным и запутанным, однако достаточно лишь понять правила расположения осей и переноса строк, а нужные значения свойств всегда можно посмотреть в различных справочниках. Хорошей практикой будет самостоятельное экспериментирование с вышеописанными свойствами.
flex-flow
Данное свойство является лишь «синтаксическим сахаром», оно позволяет одновременно указать два предыдущих свойства, которые мы изучили — направление осей и многострочность. Синтаксис может быть следующий:
<style>
.container{
flex-flow: row wrap;
flex-flow: column nowrap;
flex-flow: column-reverse wrap-reverse;
flex-flow: wrap; (4)
flex-flow: initial; (5)
}
</style>
Сначала указывается направление главной оси, и это, разумеется, может быть одно из ключевых слов-значений свойства flex-direction, либо глобальное значение. Второе значение — многострочность flex-wrap.
В случае, если указано только одно значение, CSS сам определяет, о чём идёт речь. Если указанное значение подходит для flex-direction, то применяется для него, а flex-wrap берётся по умолчанию (в строке 5 так и происходит). Если значение подходит для flex-wrap, то берётся это значение, а flex-direction берётся по умолчанию (строка 4).
Важно понимать, что при указании flex-direction и flex-flow они будут перезаписывать друг друга, даже если для flex-flow не указано нужное значение:
CSS будет работать так: у нас есть flex-direction, применяем его. Смотрим дальше и видим, что там есть flex-flow со значением wrap. Это значение относится к переносу строки, но не к направлению. Поэтому в данном свойстве flex-direction будет определяться как initial, то есть как row. Вышестоящее значение flex-direction будет перезаписано, результат отображения — главная ось по умолчанию, элементы переносятся на новые строки.
justify-content
Для горизонтального выравнивания, а точнее выравнивания по главной оси, используется свойство justify-content. Оно позволяет нам располагать флекс-элементы слева, справа, в начале, в конце оси, по центру, равномерно по ширине родителя и пр. По умолчанию задано значение flex-start (normal), это то самое поведение, что прежде вы наблюдали на картинках — элементы располагаются в начале оси и следуют друг за другом согласно их положению в разметке или индексу order. Но также могут быть заданы и другие значения.
Прежде чем мы изучим их, следует рассказать о свободном пространстве флекс контейнера, ведь это именно то, с чем будут работать горизонтальное и вертикальное выравнивания. Когда мы определяем ширину контейнера как 500px, мы получаем довольно много места, которое не занято квадратами, ведь флекс элементы по умолчанию стремятся сжаться до минимума. То, что остаётся не занятым — и будет свободным пространством флекс-контейнера.
Это важно, потому что дальше мы будем работать с этим свободным пространством. Может быть, конечно, и ситуация, когда свободного пространства вообще нет, например, если мы задали для четырёх флекс-элементов размеры по 25%.
Итак, по умолчанию элементы выравниваются по значению flex-start, это означает, что они расположены друг за другом с начала главной оси и далее по её направлению (слева направо, справа налево и пр.). Этот пример мы рассматривать не будем, поскольку всё, что вы видели выше, как раз и было justify-content:flex-start
Аналогично работает и flex-end, только располагает элементы в конце оси. При этом порядок расположения элементов сохраняется.
Мы имеем направление оси слева направо, а выравнивание — в конце оси. Порядок расположения элементов сохраняется — красный, синий, зелёный.
При переопределении направления оси меняется порядок и расположение элементов — ведь ось теперь начинается справа и её конец находится слева. Расположение изменилось визуально, но оно также следует оси — справа налево красный, синий, зелёный:
Особенным образом работают значения left и right. Поначалу можно даже подумать, что они повторяют flex-start и flex-end, однако это вовсе не так. Эти значения работают так, что элементы в любом случае располагаются слева или справа, независимо от направления главной оси! Изменяется лишь порядок элементов вместе с осью. Это может быть удобно, когда мы захотим изменять направление главной оси, но при этом желаем, чтобы элементы всегда оставались слева\справа.
Даже если мы переключим flex-direction, элементы всё равно останутся слева.
Прочие способы выравнивания касаются как раз свободного пространства. justify-content:center размещает все элементы посередине контейнера, распределяя свободное пространство слева и справа от них.
justify-content:space-between размещает крайние элементы по краям, а свободное пространство распределяет равномерно между всеми остальными элементами:
justify-content:space-around размещает элементы таким образом, чтобы отступы слева и справа всех элементов были одинаковыми, при этом они «не схлопываются», как и горизонтальная маржа:
justify-content: space-evenly размещает элементы так, что отступы между всеми элементами, включая крайние, были одинаковыми:
Вы не должны заучивать все эти значения, достаточно понимания, на что способны флексбоксы, а нужные свойства и объяснения вы всегда можете посмотреть в справочнике. Со временем и практикой, конечно, вы запомните и усвоите весь этот материал.
flex-start (normal) | элементы расположены друг за другом с начала главной оси согласно её направлению, слева направо, справа налево и пр. |
flex-end | элементы расположены друг за другом с конца главной оси согласно её направлению |
left | элементы расположены слева, независимо отнаправления главной оси |
right | элементы расположены справа, независимо отнаправления главной оси |
center | элементы выравниваются посередине строки, свободное пространство распределяется поровну слева и справа от них |
space-between | крайние элементы выравниваются по краям флекс-контейнера, а свободное пространство равномерно распределяется между всеми прочими элементами |
space-around | свободное пространство распределяется так, что отступы между элементами слева и справа одинаковы (при этом отступы не схлопываются) |
space-evenly | свободное пространство распределяется так, что отступы между всеми элементами одинаковы |
inherit | Наследует значение от родителя |
initial | принимает значение по умолчанию |
unset | значение не установлено. Поскольку свойство не является наследуемым, принимает значение flex-start (normal) |
Маржа, указанная для флекс-элементов, будет учитываться при выравнивании, но она не будет являться частью свободного пространства. Пример будет нагляднее:
Мы выровняли квадраты по центру, но задали для некоторых из них отступ справа. В результате элементы сдвинулись и мы можем взглянуть на эту маржу как на часть элементов. Свободное пространство в контейнере слева и справа от элементов остаётся одинаковым.
Использование маржи очень удобно, когда мы хотим задать выравнивание для элементов, но немного сдвинуть какой-либо из них в любую сторону.
align-items
Подобно выравниванию по горизонтали по главной оси, существует также вертикальное выравнивание элементов. Мы можем выровнять элементы по базовой линии, по центру, растянуть и пр. За это отвечает свойство align-items. Значение по умолчанию — stretch, и это как раз то, что мы уже видели: элементы растягиваются на всю высоту флекс-контейнера. Однако для этого должен быть соблюдён ряд условий: высота контейнера должна быть хоть какая-то, высота элементов не должна быть явно задана. То есть, если у нас элементы формируют высоту контейнера, то, хотя они формально и растянуты, нам видится это не так. Если же мы зададим для контейнера явно высоту, его элементы, даже без контента, будут растягиваться.
Чтобы продемонстрировать это, мы укажем для контейнера высоту в 100px, а для одного из квадратов вернём высоту по умолчанию, то есть height: auto. Теперь видно, что ко всем флекс-элементам применяется align-items: stretch.
Красный блок растянулся на всю высоту родителя. Другие блоки не сделали этого, потому что для них явно задана высота.
Другие значения — это flex-start,flex-end,center,baseline. С первыми двумя должно быть всё понятно — элементы выравниваются по верху или по низу контейнера (строки). С center тоже всё довольно просто: элементы выравниваются по центру строки.
А вот с baseline может быть немного непонятно. Очевидно, что здесь выравнивание идёт по базовой линии, но что такое базовая линия и как это будет выглядеть? Наглядный пример будет с текстом в качестве флекс элемента (да, текст тоже может быть флекс-элементом и к нему применяются все те же правила, что и для тегов!):
Baseline сработает таким образом, что элементы будут выравниваться по общей базовой линии, поэтому текст на картинке немного спустился вниз, чтобы его базовая линия совпадала с базовой линией квадратов. Если мы зададим flex-start или flex-end, то все элементы будут вверху или внизу контейнера, но в любом случае поведение будет отличаться от baseline.
stretch (normal) | значение по умолчанию. Элементы растягиваются на всю высоту контейнера |
center | элементы выравниваются по центру контейнера (строки) |
baseline | элементы выравниваются по общей базовой линии |
flex-start | элементы выравниваются по верхней линии контейнера |
flex-end | элементы выравниваются по нижней линии контейнера |
inherit | Наследует значение от родителя |
initial | принимает значение по умолчанию |
unset | значение не установлено. Поскольку свойство не является наследуемым, принимает значение stretch (normal) |
Мы рассмотрели довольно простые примеры выравнивания, когда у нас есть только одна строка с элементами. Это конечно наиболее частая ситуация, но далеко не исключительная. А что будет, если строк во флекс контейнере будет много? Мы не спроста писали выше «выравнивается по центру строки». При align-items элементы действительно выравниваются по центру своих строк, но не всего контейнера!
Для наглядности добавим больше квадратов, сделаем их девять. Увеличим высоту контейнера до 400px для наглядности и зададим align-items:center. Ожидаете, что все элементы выравняются по вертикали по центру контейнера? Как бы не так!
То, что вы видите, это выровненные по центру флекс-элементы, но они выравнены по центру своих строк! Выделим строки для наглядности.
Теперь видно, как работает флекс. Строки следуют одна за другой, поровню деля высоту контейнера. Затем при помощи align-items мы задаём выравнивание, и оно применяется в пределах строк. Это совершенно нормально. Пожалуйста, запомните это, чтобы потом не быть сбитым с толку.
align-content
В случае, когда мы хотим выравнять также строки по вертикали, мы должны использовать свойство align-content. Значения и логика их работы точно такая же, как и для justify-content, только применяется всё к вертикальной (поперечной) оси.
Прочие значения, как space-between, space-evenly тоже работают. Останавливаться на этом подробно не будем, поэкспериментируйте самостоятельно, чтобы закрепить этот материал!
Если мы указываем flex-direction: column, то происходит всё то же самое, только главная ось поворачивается на 90 градусов.
Несмотря на это, как вы можете понять, justify-content всё ещё применяется к ней, а align-content к бывшей вертикальной оси, то есть практически для нас ничего, кроме направления оси, не изменилось.
stretch | значение по умолчанию, строки растягиваются, поровну деля высоту\ширину контейнера |
flex-start | строки расположены друг за другом с верхнего края главной оси |
flex-end | строки расположены друг за другом с конца главной оси |
center | строки выравниваются посередине контейнера, свободное пространство распределяется поровну сверху и снизу (слева и справа) от них |
baseline | строки выравниваются по базовой линии |
space-between | крайние строки выравниваются по краям флекс-контейнера, а свободное пространство равномерно распределяется между всеми прочими строками |
space-around | свободное пространство распределяется так, что отступы между строками одинаковы (при этом отступы не схлопываются) |
space-evenly | свободное пространство распределяется так, что отступы между всеми строками одинаковы |
inherit | Наследует значение от родителя |
initial | принимает значение по умолчанию |
unset | значение не установлено. Поскольку свойство не является наследуемым, принимает значение stretch |