Привет, Хабр. Я продолжаю отвечать на вопросы из собеседований на должность фронтендера. Сегодня я отвечу на следующий вопрос: «В чём отличия между значениями block
, inline
, flex
, inline-flex
, grid
и inline-grid
для свойства display
?»
Прошу внимания. Мой ответ будет основан на теории, которую я описал в статье «Зачем нужно использовать свойство display
?». Прочитайте, пожалуйста, сначала её.
Последний технический момент. Во всех примерах я использую <body>
в качестве родительского контейнера. На картинках он обозначен голубой пунктирной линией.
А теперь переходим к статье.
Ключевое слово flow
Ключевое слово flow является базовым. Именно благодаря ему вы узнали про разницу между «блочными» и «строчными» элементами. Посмотрим, в чём же разница.
▍ Начальное расположение
Во всех курсах, попадавшихся мне, при рассмотрении свойства display
есть одна общая черта. Преподаватели говорили, что элементы могут располагаться друг под другом или друг за другом. Показывали примеры со значением block
и inline
.
И у меня всегда был вопрос: «А как же значения flex
, grid
,inline-flex
и inline-grid
?» Ведь в них же тоже используются ключевые слова block
и inline
.
Мы не будем поступать так же. Рассмотрим все значения. Первыми будут — block
, flex
и grid
.
Элементы выстроились в столбец. И так получается, потому что у каждого значения используется ключевое слово block
.
Похожий алгоритм существует и при ключевом слове inline
. В значениях inline
, inline-flex
и inline-grid
оно выстраивает элементы в строку.
Если элементы не помещаются внутри родителя, то они переносятся на новую строку. Правда, есть разница. При значении inline
элемент может частью остаться на одной строке, а другой перенесётся.
Оставшиеся значения такого не позволяют. Элементы полностью переносятся на новую строку.
Так произошло, потому что значение inline
является особым. Оно создаёт inline box
элемент, а не inline-level
, как все остальные inline-*
значения. И это не единственный случай выделения inline
значения. Далее мы увидим другие.
▍ Свойства width
и height
Говоря о данных свойства, нужно учитывать, что значение для них определённо всегда. Либо мы явно это делаем, либо браузеры делают это за нас неявно. Сначала рассмотрим пример, в котором нет установленного значения.
Значение свойства width
рассчитано относительно такого же свойства у родительского элемента. Проще говоря, они равны. А значение свойства height
зависит от высоты контента.
Теперь попробуем установить значения сами. Например, 100px
.
Браузеры применили его корректно. Запомните, пожалуйста, этот момент. Далее он нам пригодится.
При использовании ключевого слова inline
свойства width
и height
рассчитываются в зависимости от размеров контента элемента.
Попробуем явно установить те же значения для свойств width
и height
, какие использовали для ключевого слова block
.
Cюрприз. Элемент со значением inline
не поменял свои размеры, а все остальные это сделали.
▍ Свойство margin
У свойства margin
вместе с ключевым словом block
есть несколько неожиданных нюансов. С большой вероятностью вы слышали про схлопывание внешних отступов. Посмотрим ещё раз, к чему приводит данное поведение, установив margin: 10px
для всех элементов.
Отступ между элементом со значением block
и элементом со значением flex
— 10px
, а не 20px
. Получается, браузеры не суммируют значения. Они их накладывают. А потом выбирают наибольшее.
Также к этому явлению относится случай, когда значения, установленные для дочерних элементов, выходят за границы родителя сверху и снизу. В нашем примере так происходит у элемента со значением block
и grid
.
Хорошо, что это поведение можно исправить. Свойства padding
, border
и overflow
отменяют схлопывание после добавления их к родителю. В наш пример я добавлю padding: 10px
к элементу <body>
.
Свойство margin
можно использовать так, чтобы браузеры динамически рассчитывали значение от границ родителя. Есть значение auto
. По классике тут есть нюансы.
Вы же помните, что по умолчанию свойство width
равно значению свойства width
родителя? Другими словами, для расчётов значения auto
нет места. Так что мы обязаны установить значение для свойства width
. Я буду использовать 100px
.
Второй нюанс заключается в том, что по вертикали значение не рассчитается. Для демонстрации этого поведения я установлю height: 200px
для элемента <body>
.
Ух, с ключевым словом block
закончили. На очереди ключевое слово inline
. Тут тоже есть свои нюансы. Определю margin: 10px
для всех элементов.
Свойство margin
у элемента со значением inline
выходит за пределы родителя по вертикали. Это поведение напоминает случай с ключевым словом block
.
Только есть разница. Это поведение отменить нельзя. У остальных элементов свойство отображается корректно. Дополнительно скажу, что так же будут отображены свойства padding
и border
.
А что со значением auto
? Его не получится использовать. Браузеры игнорируют margin: auto
.
▍ Свойство text-align
С помощью margin
нельзя позиционировать элементы с ключевым словом inline
. Как же быть? Свойство text-align
служит для этой задачи.
Для примера я установлю text-align: center
для элемента <body>
.
Ключевое слово flex
У ключевых слов block
и inline
много неочевидных моментов. Поэтому я радовался, как ребёнок, когда появилось ключевое слово flex
. Ведь теперь проблем будет меньше! Сейчас всё покажу.
В своём рассказе я буду использовать два термина. Флекс-контейнер и флекс-элемент. Первый обозначает родительский элемент, к которому применено значение flex
или inline-flex
. Второй означает дочерний элемент внутри флекс-контейнера.
Что ж, давайте начнём.
▍ Начальное расположение
Давайте добавим display: flex
для элемента <body>
и посмотрим, что получится.
Скорее всего, этот пример не является неожиданностью для вас. На всех курсах говорят, что внутри флекс-контейнера есть основная и дополнительная оси. Элементы выстраиваются друг за другом вдоль основной оси.
Давайте остановимся и вдумаемся в данное поведение. Что происходит?
Раньше, если нам нужно было изменить расположение элементов, мы меняли значение свойства display
. А теперь меняй, не меняй, результат не изменится. Тут всем правят оси!
По этой причине существует свойство flex-direction
, которое управляет ими. Изменяя его значение, мы получаем новое расположение элементов. Например, при значении column
элементы отобразятся в столбец.
Перенос элементов тоже был изменён. Теперь по умолчанию он отключён. Элементы всегда будут на одной строке.
Включить его можно. Дополнительное свойство flex-wrap
и значение wrap
помогут.
▍ Свойства width
и height
Внутри флекс-контейнера свойства width
и height
зависят от осей. Значение свойства по основной оси всегда рассчитывается от размеров контента, а по дополнительной — совпадает с таким же свойством у родителя.
При значении row
значение свойства width
зависит от ширины контента, а свойство height
совпадает с таким же свойством родителя. Это видно, если определить высоту для родителя.
При значении column
происходит всё наоборот.
Ещё мы можем легко установить значение для свойств без сюрпризов.
Конечно, всё гладко не могло пройти. Наверняка вы слышали про свойства flex-basis
, flex-grow
и flex-shrink
. Их алгоритм работы не входит в тему статьи. Но есть момент, который важен.
При использовании флекс-контейнера эти свойства устанавливают значение для свойств width
и height
. Только не сразу для двух. А только для того свойства, которое совпадает с основной осью. Таким образом при flex-direction: row
свойства влияют на свойство width
, а при flex-direction: column
— на свойство height
.
▍ Свойство margin
Давайте добавим margin: 10px
к флекс-элементам.
Мы видим, что браузеры отсчитывают значения от границ родительского элемента. Посмотрим, что будет с переключёнными осями. Для элемента <body>
добавим flex-direction: column
.
По-прежнему свойство margin
не может выйти за пределы родителя. А ещё значения между элементами не накладываются друг на друга, а суммируются. Таким образом, отступ между ними 20px
.
При использовании значения auto
браузеры будут равномерно распределять пространство между флекс-элементами. В этом процессе есть ряд нюансов. Рассмотрим пример с одним флекс-элементом, у которого явно не определены свойства width
и height
.
Упс. Элемент перестал тянуться по дополнительной оси! Дело в том, что для расчёта итогового значения свойства margin
требуется знать размеры элемента. А мы их же явно не указали. Вот браузеры делают так, что размеры флекс-элемента рассчитывались от контента.
Вот что будет, если задать значение 100px
явно для свойств width
и height
.
Но это не единственная особенность. Когда мы рассматривали значения auto
внутри ключевого слова flow
, у нас не было сценария, когда значение применяется к нескольким элементам, находящимся на одной строке. А внутри ключевого слова flex
это реально.
У нас будет два флекс-элемента. Для первого установим margin: auto
.
Всё доступное пространство браузеры добавили к свойству margin
. Как они рассчитали его? Они знают размеры родителя. Из них вычли размеры флекс-элементов. Итоговое значение равномерно распределили и добавили к элементу с margin: auto
, сместив оставшиеся элементы к границам родителя.
В следующей ситуации margin: auto
добавим к двум флекс-элементам.
Здесь происходит то же самое, только итоговое значение распределяется между двумя элементами.
▍ Свойство text-align
Мы рассматривали, как свойство text-align
могло выравнивать элементы внутри ключевого слова flow
. В случае с флекс-контейнером такое поведение не сработает. Но если к флекс-элементу применено ключевое слово block
, то свойство будет работать для его контента и потомков с ключевым словом inline
.
Свойство display
дочерних элементов
Внутри флекс-контейнера есть только флекс-элементы. Это означает, что если у элемента было установлены значения inline
или одно из inline-*
, то они трансформируются в блочную альтернативу.
body {
display: flex;
}
.inline {
display: inline; /* здесь значение трансформируется в block */
}
.inline-flex {
display: inline-flex; /* здесь значение трансформируется в flex */
}
.inline-grid {
display: inline-grid; /* здесь значение трансформируется в grid */
}
Ключевое слово grid
Ключевое слово flex
крутое. Только авторы стандартов не остановились на нём. А по моему мнению, прокачали, введя ключевое слово grid
. Чтобы это рассмотреть, нам понадобится два термина: грид-контейнер и грид-элемент. Первый — элемент, у которого установлено display: grid
или display: inline-grid
. Второй — дочерний элемент грид-контейнера.
▍ Начальное расположение
Добавим display: grid
к элементу <body>
и посмотрим, как браузер отобразит элементы.
Правда же напоминает ключевое слово block
? Но это ошибочное суждение. Мы легко можем изменить отображение. Свойство grid-auto-flow
со значением column
выстроит элементы в строку.
А если элементов будет слишком много, то браузеры не смогут перенести их.
Включить перенос элементов нельзя. Нам даже не помогут свойства grid-template-rows
и grid-template-columns
. Они создадут внутри грид-контейнера сетку, по которой можно расположить элементы. Но это уже не перенос элементов.
▍ Свойства width
и height
Свойства width
и height
работают хитро. Начнём со свойства, относящегося к основному направлению. Браузеры стараются распределить пространство между грид-элементами в зависимости от их количества и контента. Для этого используют специальный алгоритм. Разберём его по шагам.
Шаг первый. Определяется значение свойства grid-auto-flow
. Шаг второй. Если оно row
, то браузеры вычитают из свойства height
родителя общую высоту дочерних элементов. При значении column
берётся свойство width
грид-контейнера и из него вычитается общая ширина грид-элементов.
Шаг третий. Полученное значение делят на количество грид-элементов. Шаг четвёртый. При grid-auto-flow: row
полученное значение добавляют к свойству height
, а при grid-auto-flow: column
— к свойству width
.
По второму направлению значение свойств width
или height
также зависят от свойства grid-auto-flow
. Только при любых значениях они cовпадают со значением таких же свойств у родителя.
Звучит сложно. Мне кажется, на примере будет понятнее. В нём к элементу <body>
будет применено свойство grid-auto-flow
. На первом изображении будет итоговый результат при значении по умолчанию (row
), а на втором — при значении column
.
Мы видим, что всё пространство заполнено. Но по основному направлению второй грид-элемент больше, чем первый. Как так получилось?
Начнём с того, что у второго элемента больше контента, чем у первого, а, следовательно, и размеры. После создания грид-контейнера при grid-auto-flow: row
браузеры вычли из высоты родителя общую высоту дочерних элементов. При grid-auto-flow: column
то же самое было с шириной.
Полученное значение поделили на количество элементов, т. е. на два. А его добавили к свойству height
каждого грид-элемента в первом случае и к свойству width
во втором. В итоге получилось, что второй элемент остался больше, чем первый, но вместе они заполнили грид-контейнер.
Переходим ко второму направлению. Грид-элементы и тут заполнили всё пространство. Но поскольку на этом направлении они не встречаются друг с другом, то браузеры повторили значения свойств width
и height
родителя. Только в первом случае это было значение свойства width
, а во втором — значение свойства height
.
▍ Свойство margin
Внутри грид-контейнера свойство работает во многом так же, как во флекс-контейнере. Значения свойства margin
между соседними элементами суммируются, а крайние — рассчитываются от границы родителя.
Значение auto
работает по двум осям в зависимости от свободного пространства. При его использовании браузеры рассчитают значения свойств width
и height
сами, основываясь на размерах контента.
Разница заключается в том, что элементы не могут сместиться к границам родителя. Они остаются на своём месте. Это лучше всего заметно при grid-auto-flow: column
.
▍ Свойство text-align
Cвойство text-align
для грид-контейнера работает так же, как для флекс-контейнера. Соответственно, оно влияет на контент и элементы с ключевым словом inline
, находящиеся в грид-элементе.
▍ Свойство display
дочерних элементов
По аналогии с ключевым словом flex
внутри грид-контейнера грид-элементы приобретают блочные варианты значения для свойства display
. Происходит такая же трансформация, поэтому не буду показывать её.
Заключение
Осталось перечислить отличия.
Отличие №1. Внутри элемента с ключевым словом flow
могут существовать элементы со значениями inline
, inline-flex
и inline-grid
. Внутри ключевого слова flex
и grid
их не будет. Они заменяются на блочную альтернативу.
Отличие №2. В случае использования ключевого слова flow
позиция элемента меняется при помощи значений свойства display
. При ключевых словах flex
и grid
используются дополнительные свойства flex-direction
и grid-auto-flow
.
Отличие №3. При ключевых словах flex
и grid
перенос элементов по умолчанию отключён. Включить его можно только для ключевого слова flex
. При ключевом слове flow
он есть сразу.
Отличие №4. Когда используются ключевые слова flow
и flex
для расчёта значения свойств width
и height
, браузеры используют родительское значение этих свойств или размеры контента элемента. При ключевом слове grid
значения ещё зависят от количества элементов в родителе.
Отличие №5. Если определить значение для свойств width
и height
у элемента со значением inline
, то браузеры не применят его. При остальных значениях так не будет.
Отличие №6. Значение auto
не срабатывает для элементов со значениями inline
и inline-*
. Для других оно работает.
Отличие №7. Значение auto
работает по двум осям внутри ключевых слов flex
и grid
. При ключевом слове flow
только по одной.
Отличие №8. При ключевых словах flex
и grid
, установленных у родителя, значение auto
заставляет браузер пересчитать значения свойств width
и height
по контенту для дочерних элементов. При ключевом слове flow
такого нет.
Отличие №9. Значение auto
распределяет равномерно между элементами при ключевых словах flex
и grid
. Только при ключевом слове flex
могут сместиться соседние элементы, при ключевом слове grid
такого нет. С ключевым словом flow
распределения пространства нет.
Отличие №10. Свойство margin
может выходить за границы родителя и накладываться друг на друга внутри ключевого слова flow
. При ключевых словах flex
и grid
такого поведения нет.
Отличие №11. Свойство text-align
работают вместе с ключевым словом flow
. С ключевыми словами flex
и grid
оно игнорируется.
P.S. Присоединяйтесь к моему ТГ каналу CSS isn't magic. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Spaceoddity
Ну уж нет!. Не стоит всё так упрощать)) Рассчитанное значение height - это довольно сложная абстракция. Вспомните хотя бы самую типовую задачу "прибить футер к низу вьюпорта", со всякими min-height, height: auto и т.п. И это мы ещё позиционирование в расчёт не брали...
melnik909 Автор
Тут я понимаю, что забыл, как прибивал футер....
Spaceoddity