10 февраля 2020

В этом руководстве будут раскрыты различия, плюсы и минусы, а также выработаны рекомендации по использованию float, inline-block, display: table и flexbox при построении отзывчивых (responsive) CSS-макетов. Вот некоторые из методов, описанных в руководстве:

  • Выравнивание элементов, следующих бок о бок (друг за другом)
  • Последовательное расположение
  • Горизонтальное центрирование
  • Вертикальное центрирование
  • Выравнивание высоты элементов ряда
  • Передовые техники с использованием flexbox

Предисловие

Прежде чем погрузиться в методы компоновки, рассмотрим небольшой фрагмент CSS кода, который сделает нашу жизнь бесконечно легче. Вот он:

html { box-sizing: border-box; }
*, *:before, *:after { box-sizing: inherit; }

Я добавляю это во все мои CSS документы. Таким образом я могу добавить элементу отступ и границы, и это не повлияет на его высоту и/или ширину, которые я ему установил.

Например, если я устанавливаю элементу свойство width: 50%;, а затем добавляю padding: 20px; , элемент останется 50% в ширину.

Без box-size: border-box;, к общей ширине будет добавлено 40px (по 20px c левой и правой сторон), поэтому элемент, в действительности, будет 50%+40px в ширину, что может вызвать приступ мигрени и делает отзывчивый CSS практически невозможным.

Во всех примерах я буду использовать .parent и .child в качестве классов CSS. Эти классы используются для иллюстрации структуры HTML элементов (родительского и дочернего). Например:

<div class="parent">
  <div class="child"><!-- content goes here --></div>
</div>

Все примеры будут сопровождаться демонстрационными вставками. Эти демонстрации будут содержать немного больше CSS кода, чем это необходимо, для достижения желаемой разметки границ или цвета фона. Это сделано для того, чтобы лучше проиллюстрировать размеры, расположение и расстояние между элементами.

Метод 1: floats (плавающие элементы)

Большинство HTML элементов (div, section, header, h1, p, ul, li и т.д.) по умолчанию имеют значение свойства display равным block.

display: block;

Такие элементы по умолчанию занимают 100% ширины их контекста. Это делает невозможным расположение двух таких элементов рядом друг с другом без дополнительного CSS кода.

Этот метод требует добавления всего одной строки CSS кода для всех элементов с display: block; — float: left; или float: right;

Пример 1

Это два расположенных бок о бок элемента div c шириной равной 50%.

Пример 2

Мы также можем легко создать пространство между двумя или более равномерно распределенными элементами с использованием свойства margin с правой стороны каждого элемента (margin-right). Кроме того, поскольку пространство нам нужно только между элементами, а не снаружи всего ряда, мы можем использовать селектор :nth-child() для таргетинга последнего элемента в каждом ряду.

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

Пример 3

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

Пример 4

Использование float также является идеальным способом компоновки для реализации обтекания изображения текстом. Объявление float удаляет блочный элемент из нормального потока документа, что означает, что последующие блочные элементы (например, такие как параграф — p), будут "проскальзывать" под ним, но текст внутри этих элементов будет обтекать плавающий элемент.

Однако, чтобы эффективно использовать этот метод, вам нужно добавить так называемый «clearfix hack» к любому родительскому элементу, который содержит плавающие элементы. Без этого вы не сможете увидеть фон родительского контейнера, а также можете столкнуться с другими проблемами компоновки.

Плюсы и минусы использования метода floats

Преимущества

  • Поддерживается всеми браузерами.
  • Простой способ "расталкивания" элементов влево и вправо внутри родительского контейнера.
  • Простой способ реализации обтекания изображения текстом.
  • Элемент с float:right будет помещен вторым по порядку (справа) на двух колоночной странице, несмотря на то, что он является первым элементом в исходном HTML коде. Иногда это может пригодиться.

Недостатки

  • Не может быть использовано для центрирования (горизонтального или вертикального).
  • Чтобы избежать проблем с отображением и компоновкой, требуется использование «clearfix hack» в родительском контейнере.
  • Float-элемент с фиксированной шириной (px) рядом с float-элементом c динамической шириной (%) может вызвать проблемы с компоновкой, если доступного горизонтального пространства окажется недостаточно.

Рекомендации

Этот метод компоновки отлично подходит в том случае, когда вам нужно реализовать обтекание картинки текстом. Например, комбинация float:right и float:left может быть использована для фиксации стрелок в режиме слайд-шоу.
Он также отлично работает с последовательностью элементов, которые следуют один за другим или для компоновки больших контейнеров макета (заголовок, боковая панель, основная область содержимого и нижний колонтитул).

Метод 2: inline-block

Встроенный блок (inline-block) представляет собой смесь двух режимов отображения — встраивания и блоков. С помощью этого метода можно создавать макеты, лишь немногим отличающиеся от тех, которые вы можете создать с помощью метода floats. Фактически, многие не-flexbox CSS фреймворки используют в качестве основных методов построения сетки либо inline-block, либо float.
В отличие от метода floats, здесь вам не нужно очищать родительский контейнер, потому что он не удаляется из потока документа. Но inline-block не лишен своих странностей.
Например, если вы установили двум контейнерам свойство display: inline-block; и ширину до 50%, вы можете обнаружить, что второй контейнер перешел на следующую строку.

Пример 5

Теоретически эти два контейнера должны располагаться бок о бок. Тем не менее, inline-block наследует характеристики inline элементов, и когда между inline элементами в html существуют пробелы (или символ перевода строки), браузер эти пробелы отображает.
Таким образом, лишние 4 (или около того) пикселя пробелов заставляют второй контейнер переходить на следующую строку.

Как решить эту проблему? Удалите в вашем HTML пробелы между закрывающими и открывающими тегами элементов inline блока. В блоге Дэвида Уолша описано еще несколько способов, как справиться с этим.
PureCSS (purecss.io), легкий адаптивный фреймворк, удаляет лишние пробелы, используя "negative letter" или межстрочный интервал (в зависимости от браузера).
Это работает, но применение этих методик требует соблюдения некоторых других правил, поэтому обязательно изучите документацию.

Пример 6

Одним из преимуществ inline-block является простота горизонтального центрирования. Из-за их inline характеристик вы можете использовать для центрирования установку значения свойства text-align: center; для родительского элемента.

Однако заметьте, что мне пришлось сбросить выравнивание текста в дочернем контейнере, если я хочу, чтобы текст внутри этих контейнеров был выровнен по левому краю. Не идеально, но это работает.

Пример 7

Еще одним преимуществом inline-block является возможность выравнивания по вертикали (vertical-align) по верхнему и нижнему краю или же посередине (значения top, bottom и middle).

Эти элементы центрируются вертикально относительно друг друга, а не по отношению к родительскому контейнеру. Тем не менее, если у вас есть только одна строка элементов, они будут технически вертикально центрироваться внутри родительского контейнера, если значения свойств padding-top и padding-bottom родительского контейнера одинаковы.

Плюсы и минусы использования метода inline-block

Преимущества

  • Поддерживается во всех браузерах.
  • Возможность горизонтального и вертикального центрирования.
  • Можно использовать выравнивание по вертикали для выравнивания элементов разной высоты в строке по верхнему, нижнему краю или по центру.

Недостатки

  • При наличии дополнительного пространства в HTML коде возникают пробелы.
  • Невозможно легко прижать два inline-block элемента к левому и правому краю родительского контейнера.
  • Необходимо переопределить text-align: center; дочерних контейнеров, иначе текст внутри этих контейнеров будет также центрирован.

Рекомендации

В целом, inline-block — прекрасное решение для таких вещей, как навигационные элементы, сетки миниатюр видео, индикаторы разбивки на слайд-шоу и любые другие элементы, которые необходимо выстраивать в ряд бок о бок, с возможностью вертикального и горизонтального центрирования.

Метод 3: таблицы (tables)

HTML-таблицы долгое время использовались в качестве макетов структуры документа, но затем, когда разработчики начали понимать, что если мы хотим создать практичный, доступный для поиска веб, мы должны убедиться, что наш HTML-код правильно размечает нашу информацию, на первые роли вышел CSS. Таким образом, HTML-таблицы, как техника разметки макета, исчезли.

Тем не менее, использование таблиц в качестве инструмента компоновки имело несколько приятных моментов. Поэтому, чтобы сделать возможным использование некоторых методов компоновки с использованием таблиц, но без необходимости разрушать семантическую структуру HTML, было введено значение свойства display равное table (display: table;).

При использовании display: table; необходимо иметь в виду одну вещь: вы используете это значение свойства display в родительском элементе — наряду с использованием в дочерних элементах других табличных значений, таких как display: table-cell; или display: table-row; — в зависимости от того, как вы хотите их разместить.

Пример 8

Подождите? Что тут происходит? Почему оба контейнера имеют ширину 50%?

Что ж, дело в том, что ячейки таблицы будут растягиваться, чтобы занять оставшееся пространство таблицы. Именно так реагирует ячейка в реальной HTML-таблице. В сущности, ячейки таблицы плохо реагируют на установку ширины в процентах. Однако, с помощью display: table-cell; вы можете получить элемент с фиксированной шириной и соседним элементом, который займет оставшееся пространство.

Пример 9

Это то, с чем ни плавающие ни встроенные блоки не справляются без небольших "танцев с бубном".

Кроме этого, плавающие и встроенные блоки, следующие друг за другом, по умолчанию не могут быть одинаковой высоты (за исключением случаев с одинаковым по высоте содержимым). Вы можете заметить, что высота контейнеров в двух последних примерах равна высоте самого высокого из них. Именно так ведут себя ячейки таблицы для того, чтобы все элементы ряда начинались с одной и той же линии, что, в свою очередь, упрощает чтение табличных данных.

Плюсы и минусы использования таблиц

Преимущества

  • Все столбцы в ряду имеют одинаковую высоту, равную высоте самого высокого из них.
  • Вы можете использовать ячейку статической ширины рядом с ячейкой с динамической шириной без боязни, что вторая будет смещена на следующую строку.
  • Вы можете разместить по бокам два контейнера с фиксированной шириной, если поместите между ними контейнер с шириной, равной auto.

Недостатки

  • По умолчанию ячейки таблицы имеют одинаковую высоту.
  • Ячейки таблиц всегда растягиваются так, чтобы охватить всю ширину строки, если только вы не указали фиксированную, не процентную ширину.

Рекомендации

В целом, display: table; лучше всего подходит для больших контейнеров. Например, боковая панель статической ширины рядом с "гибким" контейнером основного контента. Также таблицы можно использовать, когда вы хотите получить ряд одинаковых по высоте элементов. Тем не менее, неспособность хорошо работать с шириной, выраженной в процентах, представляет собой серьезное ограничение.

Метод 4: flexbox

Flexbox объединяет все плюсы каждого из предыдущих методов в одну невероятно надежную систему компоновки.
Все начинается с объявления display: flex; в родительском контейнере. С этого момента в вашем распоряжении оказывается множество свойств и их значений, применимых как к родительскому, так и к дочерним элементам, для создания того типа макета, который вы хотите.

Пример 10

Мы объявляем display: flex; родительскому контейнеру вместе с justify-content: space-between;, чтобы обозначить дополнительное пространство между двумя дочерними элементами, которое эффективно "расталкивает" их к противоположным сторонам родителя.

Также обратите внимание, что вместо объявления ширины мы используем flex: 0 1 25%; для дочерних элементов.

Свойство flex, объявленное для дочерних (или flex) элементов, содержит три значения разных подсвойств: flex-grow, flex-shrink и flex-basis.

Дефолтные значения для любого flex-элемента следующие: flex: 0 1 auto;

Таким образом, по умолчанию браузер как-бы "говорит" flex-элементу: «Если есть лишнее пространство, не расти (flex-grow: 0;), а если пространства недостаточно, уменьшись на 1 единицу (flex- shrink: 1;). Кроме того, я хочу, чтобы твоя начальная ширина была автоматической (flex-basis: auto;)».

Мы могли бы объявить все три значения отдельно, но объявление их всех в рамках одной декларации flex смотрится более красивым и аккуратным способом.

Итак, когда вы объявляете display: flex; родительскому контейнеру, все дочерние элементы будут располагаться рядом друг с другом и занимать равную ширину внутри родительского элемента. И поскольку flex-basis по умолчанию равно auto, будет занято всё свободное пространство.

В 10-м примере мы установили flex-basis равным 25%, а поскольку flex-grow установлен в 0, дочерние элементы не будут расти больше значения flex-basis (или начальной ширины).

Пример 11

И flex-grow, и flex-shrink могут иметь либо процентные значения, либо не описательные пропорциональные значения. Рассмотрим это на примере:

Здесь у обоих дочерних элементов значение flex: basis равно 25%, так что это ширина, которую они будут занимать по умолчанию. Однако, поскольку есть только два дочерних элемента, в родительском контейнере остается 50% свободного пространства.

Поскольку мы объявили значение flex-grow равным 1 для элемента .child-1 и равным 2 для .child-2, .child-2 будет занимать в два раза больше оставшегося свободного пространства, чем .child-1.
Свойство flex может быть самой запутанной частью метода flexbox. Запомните, что flex-basis — это начальная ширина, flex-grow — это то, насколько каждому дочернему элементу разрешено расти, а flex-shrink — насколько они сократятся, если будет недостаточно места.

Пример 12

Давайте посмотрим на центрирование двух flex-элементов:

По умолчанию все flex-элементы будут растянуты по вертикали и станут такими же высокими, как и самый высокий из них, как при использовании display: table; в 8-м примере.

Это связано с тем, что родительский контейнер имеет значение align-items: stretch;, объявленное по умолчанию.

Мы можем переключить его, объявив любое из следующих значений для свойства align-items, и в каждом случае их высоты будут определяться количеством содержимого, которое они содержат, а не растягиванием:

  • flex-start — вертикально выравнивает flex-элементы по верхней границе родительского контейнера
  • flex-end — вертикально выравнивает flex-элементы по нижней границе родительского контейнера
  • center — вертикально центрирует элементы гибкости

Пример 13

Итак, если мы хотим получить два горизонтально и вертикально центрированных дочерних элемента с шириной 25%, мы могли бы сделать это так:

Пример 14

Как насчет вертикально центрированных, но закрепленных на противоположных сторонах родительского контейнера?

Пример 15

Как насчет равного пространства вокруг них и выравнивания по нижней границе родительского контейнера?

Как можно увидеть, простое изменение значений свойств justify-content и align-items в родительском flex-контейнере дает нам много вариантов компоновки.

Пример 16

С помощью flexbox можно несколькими способами переключить начальный порядок элементов. Например, это можно сделать, изменив значение свойства flex-direction.
По умолчанию свойство flex-direction имеет значение row. Но мы можем объявить flex-direction: row-reverse;, что заставит последний HTML-элемент появиться в строке первым, а первый появится последним.

Пример 17

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

Пример 18

С несколькими строками элементов в игру вступает свойство align-content.
Допустим, у вас есть родительский flex-контейнер с высотой, равной 100% высоты viewport, а также два ряда с шириной 25%. Как же одновременно вертикально центрировать эти две строки? Вот так:

Здесь мы устанавливаем высоту контейнера равной высоте viewport. Затем мы объявляем flex-wrap: wrap; для принудительного переноса элементов на новую строку, если в текущей строке недостаточно места для них.

Затем мы используем align-items: flex-start;, чтобы убедиться, что flex-элементы не "растягиваются" по высоте, и используем align-content: center;, чтобы центрировать обе строки одновременно.

Этот пример является довольно сложным, хотя в действительности он лишь начинает раскрывать все возможности flexbox.

Фактически, вы можете сделать все то же, что мы сделали до этого, только в вертикальной ориентации, просто объявив flex-direction: column; в родительском контейнере. После этого все объявления justify-content будут касаться разделения элементов по вертикали.

Изменение свойства flex-direction считается изменением первичной оси. Следовательно, значения свойства justify-content по умолчанию относятся к первичной оси, а align-items и align-content связаны со вторичной осью.

Плюсы и минусы использования flexbox

Преимущества

  • Простое объявление display: flex; в родительском контейнере дает вам дочерние элементы равной высоты и ширины.
  • Существует огромное множество возможных комбинаций всех flexbox свойств.
  • Вы можете использовать display: flex; для создания flex-элементов, которые затем будут игнорировать свойства float и inline-block, что может быть полезным, когда вам нужно предоставить фоллбеки (fallbacks) для старых браузеров.

Недостатки

  • Вы должны объединять flex-элементы в рамках родительского элемента.
  • Из-за большого количества возможностей, вначале этот метод может показаться сложным и запутанным.
  • * Поддержка браузеров по-прежнему несколько ограничена, поэтому не забудьте проверить, какие браузеры вам нужно поддерживать в проекте, и нужно ли вам использовать float или inline-block методы в качестве фоллбеков.

* на момент перевода (июнь 2018) с поддержкой современных браузеров все нормально — прим. перев.)

Рекомендации

Во-первых, убедитесь, что браузеры, которые вам нужно сопровождать поддерживают flexbox.

К счастью, вы можете добавить flexbox в существующий float или inline-block макет без особых сложностей. Например, старые браузеры будут использовать display: inline-block;, и значения свойств width и margin, в то время как новые браузеры будут использовать display: flex;, и значения свойств flex и justify-content.

Наконец, рекомендуется использовать такие инструменты и техники, как Sass mixins, PostCSS или CodeKit, которые позволят вам не возиться со всеми браузерными префиксами, связанными с определенными свойствами и значениями flexbox.

Выводы

Возможности CSS компоновки в действительности бесконечны, но использование основополагающих принципов, перечисленных в этом руководстве, поможет вам овладеть мастерством создания CSS-макетов. Мы рассмотрели множество основных сценариев, с которыми вы, вероятно, столкнетесь при создании адаптивных макетов, например, центрирование элементов как по вертикали, так и по горизонтали, размещение элементов фиксированной ширины рядом с элементами с динамической шириной и различные способы создания и компоновки строк элементов и "расталкивания" их на противоположные стороны родительского контейнера.

Перевел scorp13

Статья является свободным переводом материалов сайта http://buildawesomewebsites.com