Привет, Хабр. Я продолжаю отвечать на вопросы из собеседований на должность фронтендера. Сегодня я отвечу на следующий вопрос: «В чём отличия между значениями 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 и элементом со значением flex10px, а не 20px. Получается, браузеры не суммируют значения. Они их накладывают. А потом выбирают наибольшее.


Также к этому явлению относится случай, когда значения, установленные для дочерних элементов, выходят за границы родителя сверху и снизу. В нашем примере так происходит у элемента со значением block и grid.


Хорошо, что это поведение можно исправить. Свойства padding, border и overflow отменяют схлопывание после добавления их к родителю. В наш пример я добавлю padding: 10px к элементу <body>.


Свойство рассчитывается от области свойства padding

Свойство 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 элементы отобразятся в столбец.


Первый элемент. Под ним второй элемент

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


17 элементов в строке. 15, 16, 17 выходят за границы родителя

Включить его можно. Дополнительное свойство flex-wrap и значение wrap помогут.


17 элементов отобразились в 3 строки

▍ Свойства width и height


Внутри флекс-контейнера свойства width и height зависят от осей. Значение свойства по основной оси всегда рассчитывается от размеров контента, а по дополнительной — совпадает с таким же свойством у родителя.


При значении row значение свойства width зависит от ширины контента, а свойство height совпадает с таким же свойством родителя. Это видно, если определить высоту для родителя.


Ширина элементов рассчитана по контенту. По высоте они занимают все пространство

При значении column происходит всё наоборот.


Элементы растянулись на всю ширину родителя. Высота зависит от размера шрифта текста

Ещё мы можем легко установить значение для свойств без сюрпризов.


Элементы отображаются, как квадраты с указанными размерами 100 на 100

Конечно, всё гладко не могло пройти. Наверняка вы слышали про свойства 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.


Значение применилось. Элемент стал квадратом 100 на 100. Он расположился по центру по двум осям

Но это не единственная особенность. Когда мы рассматривали значения 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 выстроит элементы в строку.


Первый элемент. Правее второй элемент

А если элементов будет слишком много, то браузеры не смогут перенести их.


18 элементов отображаются в строку. 18 элемент выходит за границы родителя

Включить перенос элементов нельзя. Нам даже не помогут свойства 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 ?

Комментарии (3)


  1. Spaceoddity
    24.04.2024 05:53
    +1

    А значение свойства height зависит от высоты контента.

    Ну уж нет!. Не стоит всё так упрощать)) Рассчитанное значение height - это довольно сложная абстракция. Вспомните хотя бы самую типовую задачу "прибить футер к низу вьюпорта", со всякими min-height, height: auto и т.п. И это мы ещё позиционирование в расчёт не брали...


    1. melnik909 Автор
      24.04.2024 05:53

      Тут я понимаю, что забыл, как прибивал футер....


      1. Spaceoddity
        24.04.2024 05:53

        html{
        	height:100%;
        }
        body{
        	height:auto !important;
        	height:100%;
        	min-height:100%;
        }