Flexbox – быстрый путь к раскладочному блаженству?

HTML и CSS – это большой механизм предоставления контента во многих отношениях — эти технологии просты в изучении, они достаточно гибкие и мощные. Однако, есть одна вещь, в которой они никогда не преуспевали — это макет документа. Если вы хотите создать макет для простого типографского эссе, в котором будет одно или два обтекаемых текстом изображения, то всё хорошо. Но создавать сложную многоколоночную вёрстку всегда было неудобно, приходилось вставлять много костылей. А в конце вас ждало разочарование, т.к. вёрстка по-разному выглядела в разных браузерах. Мы обычно склонны злоупотреблять float-конструкциями для этих целей, но обилие багов и разница в отображении могут испортить нам всё удовольствие.

Для борьбы с этими проблемами CSS3 включил в себя множество модулей, которые помогут делать различные макеты намного проще. Мы уже рассматривали многоколоночную вёрстку и генерируемый контент для устройств постраничного вывода в других статьях. А теперь мы обратим своё внимание на Flexible Box Layout Module.

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

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

Звучит очень здорово, не правда ли? Давайте рассмотрим Flexbox в деталях.

Эта статья использует новейший синтаксис для Flexbox, на текущий момент поддерживаемый в Opera Mobile 12.1 и Chrome. Учтите, что другие браузеры, кроме Opera, имеют поддержку Flexbox версии 2009 года, но этот устаревший синтаксис больше не будет использоваться. Имейте это ввиду, когда читаете старые статьи о Flexbox.

Простой пример

Для начала рассмотрим элементарный пример, чтобы показать, насколько прост Flexbox. Мы посмотрим на конструкцию “fat footer” с тремя дочерними элементами, каждый из которых содержит дополнительно типичный footer-контент. Пусть это будет контактная информация, важные глобальные ссылки и информация об авторских правах. Мы хотим отобразить всё это внизу в горизонтальную линию, отцентрированную по вертикали. И мы хотим, чтобы ширина блока с глобальными ссылками была в два раза больше, чем ширина остальных двух дочерних элементов. На сегодняшний день мы бы, скорее всего, использовали float для дочерних элементов, установив ширину для них и корректировали бы выравнивание, используя различные padding-и. Этот способ неудобен и неточен без выставления фиксированных значений по всем измерениям (но в таком случае мы будем иметь очень негибкий макет). Но Flexbox может помочь нам тут!

figure12
Рисунок 1: пример использования FlexBox. Тут всё очень просто, кроме нижней части, которая представляет из себя гибкий макет из трёх блоков.

На заметку: для просмотра конечного результата вы можете посмотреть страницу Flexible fat footer (представленную на рисунке 1) — вы увидите, что я сделал немного “гламурненький” дизайнчик из одной колонки содержания и footer-ом в нижней части, который держится на месте магией position: fixed;. Эта вёрстка является гибкой. Ширина колонки основного содержания выставляется в процентах от ширины страницы, а дочерние элементы footer-а изменяют свой размер пропорционально главной колонке. Вы можете также заметить, что я использовал несколько media queries, чтобы адаптировать макет для маленькой ширины экрана.

Начнём использовать Flexbox

Так что же мы должны делать, чтобы начать использовать Flexbox? Большая часть свойств применяется к родительскому контейнеру располагаемых элементов. Для того, чтобы контейнер стал Flexbox-ом ему нужно указать специальное значение для display-свойства следующим образом:

footer {
  display: flex;
}

Далее мы используем flex-flow-свойство, чтобы указать, как мы хотим расположить элементы: в строку row (по умолчанию) или в колонку column. Вы также можете использовать ключевое слово wrap, если хотите, чтобы содержание перешло на новую строчку, если родительский элемент слишком мал, чтобы вместить все дочерние элементы на одной линии. Для примера, установим для нашего footer-а значение row wrap:

footer {
  display: flex;
  flex-flow: row wrap;
}

Значение ключевого слово wrap станет более очевидным чуть позже.

На заметку: flex-flow на самом деле является сокращением двух свойств: flex-direction (принимает значения row, column, row-reverse или column-reverse; последние два разворачивают вашу строку или колонку в противоположную сторону) и flex-wrap (значения wrap, no-wrap и wrap-reverse).

Главная и поперечная оси

При работе с Flexbox вы должны привыкнуть к ещё одной концепции – это существование главной (main axis) и поперечной (cross axis) оси, которые работают примерно как X и Y оси, но немного отличаются от них. Главная ось всегда направлена по направлению flex-flow, т.е. она горизонтальна при размещении дочерних элементов в строку, и вертикальна при размещении в столбец. Поперечная ось идёт всегда перпендикулярно главной оси. Эта схема иллюстрирована на рисунке 2.

flex-fig-2
Рисунок 2: иллюстрация работы с главной и поперечной осью в Flexbox.

Устанавливаем выравнивание для дочерних элементов вашего Flexbox-а

В список возможностей Flexbox входит большое количество способов выравнивания дочерних элементов по главной и поперечной оси.

align-items через поперечную ось
Прежде всего взглянем на свойство align-items, которое позволяет выравнивать дочерние элементы в Flexbox по поперечной оси. Рассмотрим значения этого свойства:

  • flex-start/baseline: верхний край всех элементов выравнивается по верху поперечной оси
  • flex-end: нижний край всех элементов выравнивается по низу поперечной оси
  • center: все элементы выравниваются по центру поперечной оси
  • stretch: все элементы растягиваются по всей длине поперечной оси

Названия говорят сами за себя. Посмотрим их в действии, поиграйте с демо-файлом, устанавливая разные значения, сверьте результат с рисунком 3. В конце этих манипуляций я закончил таким кодом:

footer {
  display: flex;
  flex-flow: row wrap;
  align-items: stretch;
}

Теперь все мои элементы будут всегда заполнять всю высоту родительского контейнера, независимо от того, чему равна его ширина и высота (которая выставляется в пропорции к размерам окна). Это просто чудесно — ведь сколько раз прежде вам бывала нужна одинаковая высота колонок с разным количеством содержимого, а приходилось выкручиваться “костылями” типа явного задания им одинаковой height или метода “ложных колонок”.

flex-fig-3a

flex-fig-3b

flex-fig-3c

flex-fig-3d
Рисунок 3: различия в использовании значений свойства align-items. Сверху вниз: flex-start, center, flex-end и stretch.

На заметку: есть также свойство align-self, которое позволяет управлять поведением align-items для отдельных элементов. Если оно задано, то оно перекрывает родительский align-items.

justify-content вдоль главной оси
Другое основное свойство в этой категории, которое мы будем использовать, называется justify-content. Оно определяет как элементы будут выравниваться вдоль главной оси. А именно, оно определяет распределение свободного пространства между элементами. Это свойство не используется, когда вы установили размеры элементов и их поля таким образом, что они заняли всю главную ось (как сделано в нашем примере). Поэтому посмотрим ещё один пример, который демонстрирует использование justify-content: Пример Flexbox с фиксированной шириной.

В этом примере для каждого дочернего элемента установлена собственная ширина, указанная в процентах:

#first {
  width: 25%;
}

#second {
  width: 40%;
}

 #third {
  width: 25%;
}

Далее установим свойство justify-content родительского контейнера следующим образом:

footer {
  display: flex;
  flex-flow: row wrap;
  align-items: stretch;
  justify-content: space-around;
}

Установленное значение будет работать очень хорошо: оно распределит всё доступное пространство между дочерними элементами и за их пределами в равных количествах. Рассмотрим другие возможные значения этого свойства:

  • flex-start: Все элементы располагаются в начале главной оси, всё свободное место находится в конце.
  • flex-end: Все элементы располагаются в конце главной оси, всё свободное место находится в начале.
  • center: Все элементы располагаются в середине главной оси, всё свободное место распределяется поровну между началом и концом.
  • space-between: значение очень похоже на space-around, но отличается от него тем, что в этом случае выделяется меньше места на каждом из концов главной оси.

Рисунок 4 демонстрирует эффект от разных значений justify-content:

flex-fig-4a

flex-fig-4b

flex-fig-4c

flex-fig-4d
Рисунок 4: различия в использовании значений свойства justify-content. Сверху вниз: flex-start, center, flex-end и space-between.

align-content для выравнивания многострочных Flexbox-ов
Вы также можете влиять на расположение между несколькими строчками дочерних элементов вашего Flexbox в случае использования ключевого слова wrap. Этот способ будет влиять на макет только в случае, когда элементы занимают фиксированное количество места в направлении поперечной оси (т.е. фиксированную высоту в случае строк). Рассмотрим доступные значения:

  • flex-start: Все элементы находятся в начале поперечной оси, всё свободное место добавляется в конец.
  • flex-end: Все элементы находятся в конце поперечной оси, всё свободное место добавляется в начало.
  • center: Все элементы находятся в центре поперечной оси, всё свободное место распределяется поровну между началом и концом.
  • space-between: Всё свободное пространство распределяется между дочерними элементами и за их пределами вдоль поперечной оси в равных количествах
  • space-around: значение очень похоже на space-around, но отличается от него тем, что в этом случае выделяется меньше места на каждом из концов главной оси.

Изменение порядка элементов макета

Так сложилось, что всегда очень сложно изменить порядок отображаемых элементов без того, чтобы изменить исходный код. Но это не про Flexbox! Flexbox позволяет устанавливать вам свойство порядка для дочерних элементов как в строке, так и в столбце. Это свойство принимает целое значение, называющееся ordinal group. Чем выше ordinal group, тем позже появится элемент. Давайте вернёмся к примеру Flexible fat footer: блок с ссылками по умолчанию располагается под номером два, как показано на рисунке 5:

flex-fig-4a
Рисунок 5: Порядок по умолчанию для элементов footer: contact, links, copyright.

По умолчанию, все дочерние элементы Flexbox-а имеют ordinal group равный нулю. Мы можем легко изменить порядок, присвоив одному из элементов новое значение ordinal group. Элемент, имеющий наибольшее значение, появится последним среди всех дочерних элементов; порядок элементов, обладающих одинаковым ordinal group будет соответствовать их порядку в исходной разметке. Внесём изменения в рассмотренный выше пример, установим ordinal group для блока links равным 1 (на рисунке 6 представлен результат):

#second {
  order: 1;
}

flex-fig-6
Рисунок 6: порядок дочерних элементов для блока footer изменён.

На заметку: вы можете использовать и отрицательные значения для свойства order

Создание ваших flex-элементов

Возможно самая мощная возможность Flexbox — это возможность устанавливать длину для дочерних элементов в направлении flex-flow (т.е. ширину при flex-flow: row и высоту при flex-flow: column). Благодаря этому мы можем менять количество места, доступного в родительском элементе в соответствующем направлении. Эта возможность реализуется благодаря свойству flex, значение которого состоит из трёх частей. Рассмотрим их все по очереди и разберёмся на что они влияют. Прежде всего, взглянем на positive flex value:

#first {
  flex: 1;
}

#second {
  flex: 1;
}

#third {
  flex: 1;
}

Это безразмерные значения, которые определяют пропорции элементов. Иначе говоря, мы решаем какое количество свободного пространства внутри родителя получит каждый дочерний элемент. Если каждый из них установлен в 1, то каждый дочерний элемент получит равный размер внутри Flexbox-а. Если вы присвоите одному из элементов значение 2, то такой элемент получит в два раза больше места, чем любой другой элемент:

#first {
  flex: 1;
}

#second {
  flex: 2;
}

#third {
  flex: 1;
}

Вы также можете установить preferred width для каждого элемента следующим образом:

#first {
  flex: 1 200px;
}

#second {
  flex: 2 300px;
}

#third {
  flex: 1 250px;
}

Прежде всего, мы установили preferred width для каждого элемента. Теперь оставшееся место слева внутри родительского элемента будет делиться в соответствии со значением positive flex value, т.е. каждый элемент получит некоторое значение ширины. Если дети имеют размер по главной оси в 200, 300 и 250 пикселей, то суммарная их ширина будет 750 пикселей. Если родительский контейнер имеет размер 950 пикселей вдоль главной оси, то в нём остаётся дополнительные 200 пикселей на пространство, распределяемое между дочерними элементами. Первый и третий элементы получат по 50 пикслов каждый, т.к. их positive flex value равно 1. Их финальная ширина будет 250 и 300 пикселов соответственно. Второй элемент получит дополнительно 100 пикселов, т.к. его positive flex value установлено 2 . Его финальная ширина составит 400 пикселов.

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

#first {
  flex: 1 1 400px;
}

#second {
  flex: 2 3 600px;
}

#third {
  flex: 1 2 400px;
}

Значение свойства negative flex value, несмотря на называние, является положительным значением — это второе безразмерное число в приведённой выше разметке. Они начинают влиять на финальное расположение элементов только в случае, когда дочерние элементы начинают выходить за пределы родительского контейнера в направлении главной оси. Они также определяют пропорции, но на этот раз это “пропорции для переполненности” (количество места, выделяемое элементу при переполнении), которая будет вычитаться из размера каждого элемента, чтобы довести общий размер всех элементов до размера родительского контейнера. Другими словами, мы избавляемся от переполнения.

Предположим, что родительский контейнер занимал 1100 пикселов вдоль главной оси. Для этого примера наши дочерние элементы будут занимать место на 300 пикселов больше (т.е. их суммарный размер по главной оси будет равен 1400 пикселов). Вот что произойдёт в соответствии с установленными значениями для свойства negative flex value:

  • Первый элемент будет усечён на 1/6 общего переполнения, т.е. на 50 пикселов. Его итоговый размер будет равен 350 пикселов.
  • Второй элемент будет усечён на 3/6 общего переполнения, т.е. на 150 пикселов. Его итоговый размер будет равен 450 пикселов.
  • Третий элемент будет усечён на 2/6 общего переполнения, т.е. на 100 пикселов. Его итоговый размер будет равен 300 пикселов.

Таким образом, более высокое значение для negative flex value будет соответствовать более маленьким элементам!

Значения, на которых я остановился в своём основном примере следующие:

#first {
  flex: 1 0 7rem;
}

#second {
  order: 1;
  flex: 2 0 8rem;
}

#third {
  flex: 1.5 0 7rem;
}

Flexbox: преимущества в отзывчивости

У нас получился действительно хороший результат в рассматриваемом примере: мы скомбинировали многострочный Flexbox (flex-flow: row wrap) с выставлением preferred flex length для его дочерних элементов (к примеру, flex: 1 0 7rem;) и media queries. При очень узкой ширине экрана мы не сможем расположить наши элементы без переполнения. Поэтому дочерние элементы Flexbox-а будут перенесены на новые строки так, чтобы это было красиво. Взгляните на рисунок 7.

flex-fig-7a

flex-fig-7b

flex-fig-7c
Рисунок 7: простое Flexbox-приложение с использованием адаптивного дизайна.

Это просто прекрасно, что такой мощности и гибкости мы можем достичь таким небольшим количеством кода!

Изящная деградация

Flexbox — полностью новый механизм раскладки, и не поддерживающие его браузеры попросту его проигнорируют. Это может стать непреодолимой помехой, из-за которой вы так и не попробуете его. Но вовсе не обязательно. Например, можно использовать float-ы или CSS-таблицы при вёрстке сайта для больших экранов, и переходить на Flexbox при очень узких окнах браузера, скажем, чтобы переместить левую колонку с навигацией под основной контент.

Если задать всем элементам страницы (навигации, хедеру, футеру и т.п.) display: block перед правилами Flexbox-а, браузер, понимающий медиавыражения, но не flexbox, просто расположит ваш контент в виде подряд идущих блоков на всю ширину экрана. Они останутся в том же порядке, что в исходнике, но весь контент будет доступен для пользователя.

Альтернатива — использовать CSS-таблицу, чтобы выдернуть элемент из его порядка в исходнике и поставить в начале или конце. Можете посмотреть грязный хак с table-caption и нервно похихикать 🙂 .

Заключение

Я надеюсь, что эта статья помогла вам понять, насколько Flexbox удивительный и полезный элемент, позволяющий нам покончить с использованием float-ов, фиксированных значений и прочих ужасных вещей. Откажитесь от хаков и негибких макетов. Кроме того, вы увидели насколько легко Flexbox позволяет реализовать отзывчивый дизайн, комбинируя его с другими технологиями, такими как Media Queries.

Изображение на обложке предоставлено Horia Varlan.

Оригинал статьи и автор. Оригинал распространяется под Creative Commons Attribution 3.0 Unported.

Поделиться с друзьями:

Блог веб-разработчика

Приветствую вас в своем блоге. Здесь вы найдете информацию о различных около-веб темах; об особенностях CMS Wordpress, о верстке CSS, программировании HTML, Javascript и так далее. Подписывайтесь на мой канал, будьте в курсе последних тенденций из мира веб.