CSS

Углубляемся в свойство flex

Spread the love

Перевод статьи: Ahmad Shadeed — Digging Into the Flex Property

Вы когда-нибудь задумывались о том, как работает свойство CSS flex? Это сокращение от flex-grow, flex-shrink и flex-base. Самый распространенный вариант использования, который я замечаю в Интернете, — это flex: 1, который позволяет элементу flex расширяться и заполнять доступное пространство.

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

Свойство Flex-Grow

flex-grow устанавливает коэффициент роста, который позволяет flex элементу увеличиваться и заполнять доступное пространство. Значение flex-grow принимает только целое число. Обратите внимание на следующее.

<div class="wrapper">
  <div class="item item-1"></div>
  <div class="item item-2"></div>
  <div class="item item-3"></div>
</div>
.wrapper {
    display: flex;
    flex-wrap: wrap;
}

.item {
    flex-grow: 1;
}

Примечание: flex-grow может влиять на ширину или высоту, в зависимости от свойства flex-direction. Для следующих примеров предположим, что для параметра flex-direction установлено значение row (значение по умолчанию), если я не уточню что-то еще.

Обратите внимание, что без использования flex-grow ширина элементов flex по умолчанию будет равна их начальной ширине. Однако при использовании flex-grow: 1 доступное пространство распределялось между ними.

Вам может быть интересно, как пространство распределяется между flex элементами? Что ж, это хороший вопрос, и я скоро на него отвечу.

На приведенном ниже рисунке показано, как элементы выглядят без flex-grow. Другими словами, это их естественный размер.

Чтобы понять, как рассчитывается ширина flex элемента, см. уравнение ниже. Я узнал об уравнении из этого поста Саманты Минг (Спасибо!).

Давайте посчитаем размер элемента, содержащего текст «CSS».

Ширина элемента (width) = ( (flex-grow / общее кол-во flex-grow) * доступное пространство) + начальная ширина элементов

Flex-grow: коэффициент увеличения flex для элемента

Total flex-grow: суммирование значения flex-grow для всех flex элементов

Available space: перед применением flex-grow

Item width: начальная ширина элемента

—> Item width = ( (1 / 3) * 498) + 77 = 241

Множественные flex-grow

В предыдущем примере значение flex-grow одинаково для всех flex элементов. Давайте попробуем добавить flex-grow: 2 для первого элемента. Как это будет рассчитываться? Имейте в виду, что доступное пространство для нашего примера составляет 498 пикселей.

Надеюсь, теперь стало понятнее.

Можем ли мы использовать 0 как значение flex-grow?

Конечно! Поскольку свойство flex-grow принимает целочисленные значения, можно использовать 0 как способ предотвратить использование flex элемента для расширения на доступное пространство.

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

Flex Grow не делает Flex элементы одинаковыми

Существует распространенное заблуждение, что использование flex-grow сделает элементы равными по ширине. Это неверно. Идея использования flex-grow заключается в распределении доступного пространства. Как вы видели в уравнении, ширина каждого гибкого элемента рассчитывается на основе его начальной ширины (ширина до применения flex-grow).

Если вы хотите, чтобы flex элементы были одинаковой ширины, это можно сделать с помощью flex-basis. Я объясню это в следующих разделах.

Свойство Flex-Shrink

flex-shrink устанавливает коэффициент уменьшения flex элемента. Если размер всех flex элементов больше, чем размер оболочки, элементы будут сжиматься в соответствии с коэффициентом flex-shrink.

Рассмотрим следующий пример. Средний элемент имеет ширину 300 пикселей, а для параметра flex-shrink установлено значение 1. Это позволит элементу уменьшиться по ширине, если нет места для размещения всех flex элементов.

.item-2 {
    width: 300px;
    flex-shrink: 1;
}

Браузер сохранит ширину элемента равной 300 пикселей при следующих условиях:

  • Сумма ширины всех элементов меньше ширины обертки
  • Ширина области просмотра равна или меньше ширины элемента

Вот как элемент ведет себя с разными размерами области просмотра.

Как вы видите на рисунке, ширина составляет 300 пикселей, пока ширина области просмотра не станет меньше 300 пикселей.

Свойство Flex-Basis

Это свойство устанавливает начальный размер flex элемента перед распределением свободного пространства в соответствии с flex коэффициентами. Размер может относиться к ширине по умолчанию и высоте (в случае flex-direction: column).

Свойство flex-basis может принимать те же значения, что и свойства width или height. Значение по умолчанию — auto, которое разрешается в content. Значение content — это автоматический размер, основанный на размере содержимого flex элемента.

.item-1 {
    flex-grow: 0;
    flex-shrink: 0;
    flex-basis: 50%;
}

В приведенном выше примере ширина первого элемента составляет 50%. Важно установить для параметра flex-grow значение 0, чтобы размер элемента не превышал 50%.

Что будет, если вместо этого использовать 100%? Элемент займет 100% ширины своего родителя, а остальные элементы будут перенесены на новую строку.

.item-1 {
    flex-grow: 0;
    flex-shrink: 0;
    flex-basis: 100%;
}

Свойство Flex

Свойство flex это сокращение от flex-grow, flex-shrink и flex-base. Значение по умолчанию для flexauto, которое разрешается как flex: 0 1 auto. Это означает, что оно позволяет flex элементам увеличиваться в зависимости от размера их содержимого.

В этом контексте есть кое-что важное, что я хотел бы выделить, а именно абсолютные и относительные flex элементы. Нет, речь идет не о позиционировании CSS, а о том, что связано с flexbox.

Относительный размер flex элементов

.item {
    /* Значение по умолчанию равно: 1 1 auto */    flex: auto;
}

В этом случае размер flex элементов будет зависить от содержимого. В результате flex элементы с большим количеством содержимого будут больше.

Абсолютный размер flex элементов

Напротив, когда для свойства flex-based установлено значение 0, все flex элементы будут увеличиваться до одинакового размера.

.item {
    /* Эквивалентно flex: 1 1 0% */    flex: 1;
}

Что мне нравится в свойстве Flex

Судя по названию, это свойство очень гибкое в принимаемых значениях. Рассмотрим следующие примеры.

Одно значение без указания единиц измерения

.item {
    flex: 1;
}

Если у него есть только одно значение, то это значение будет считаться как flex-grow.

Два значения без указания единиц измерения

.item {
    flex: 1 1;
}

Эти значения будут соответствовать flex-grow и flex-shrink соответственно. По умолчанию для flex-basis будет установлено значение 0.

Одно значение с указанием длины

Это будет использоваться для flex-basis. Для параметров flex-grow и flex-shrink по умолчанию будет установлено значение 1.

.item {
    flex: 100px;
    /* flex: 1 1 100px */}

Использование 0 без указания единиц измерения

В случаях, когда вам нужно установить flex-basis в ноль, используя сокращение flex.

.item {
    flex: 0;
}

Такое использование не рекомендуется, так как это запутает и разработчика, и браузер. Как вы можете решить, соответствует ли этот 0 коэффициентам flex (grow или shrink) или flex-basis? Это сбивает с толку. Согласно спецификации CSS:

Ноль без указания единиц измерения, которому не предшествуют два коэффициента flex, следует интерпретировать как коэффициент flex. Чтобы избежать неправильного толкования или недопустимых объявлений, авторы должны указать нулевой компонент <’flex-basis’> с единицей измерения или поставить перед ним два фактора flex.

Рекомендуется добавить единицу измерения, например px или %.

.item {
    flex: 0%;
    /* flex: 1 1 0% */}

Когда вам нужно установить grow, shrink и basis, лучше использовать для этой цели свойство flex.

Согласно спецификации CSS:

Авторам рекомендуется контролировать flexibility, используя сокращение flex, а не напрямую его свойства, так как сокращение правильно сбрасывает любые неуказанные компоненты для соответствия общему использованию.

Случаи использования

Аватар пользователя

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

<div class="user">
 <img class="user__avatar" src="shadeed.jpg" alt="" />
 <div>
  <h3>Ahmad Shadeed</h3>
  <p>Author of Debugging CSS</p>
 </div>
</div>
.user {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

.user__avatar {
    flex: 0 0 70px;
    width: 70px;
    height: 70px;
}

Обратите внимание, что я добавил для аватара flex: 0 0 70px. Это важно, поскольку в некоторых старых браузерах изображение может выглядеть сжатым, если не установлен параметр flex. Кроме того, flex имеет более высокий приоритет, чем свойство width (в случае flex-direction: row) или height (в случае flex-direction: column).

Если мы изменим размер аватара, настроив только свойство flex, width будет проигнорирована браузером.

.user__avatar {
    /* width будет 100px, а не 70px */    flex: 0 0 100px;
    width: 70px;
    height: 70px;
}

Заголовок раздела

У вас есть title для заголовка раздела, и он должен занимать все доступное пространство. В этом случае идеально подходит flex: 1.

.page-header {
    display: flex;
    flex-wrap: wrap; 
}

.page-header__title {
    flex: 1;
}

Элемент Input для отправки сообщений

Это очень распространено в приложениях для обмена сообщениями, таких как Facebook Messenger или Twitter. Входные данные должны заполнять доступное пространство с фиксированной шириной для кнопки отправки.

form {
    display: flex;
    flex-wrap: wrap; 
}

input {
    flex: 1;
    /* Другие стили */}

Приведенные выше примеры являются полезными примерами использования flex-grow. Однако некоторые случаи игнорируются или вообще не упоминаются в некоторых статьях по flex. Давайте рассмотрим их!

Выравнивание элементов на двух картах

Предположим, что CSS-сетка имеет макет из двух столбцов. Проблема в том, что положение дат не совпадают. Они должны быть на одной линии (красной).

Мы можем сделать это с помощью flexbox.

<div class="card">
 <img src="thumb.jpg" alt="">
 <h3 class="card__title">Title short</h3>
 <time class="card__date"></time>
</div>

Устанавливая flex-direction: column мы можем использовать flex-grow для заголовка, чтобы он заполнял доступное пространство и, таким образом, размещал дату в конце, даже если заголовок короткий.

.card {
    display: flex;
    flex-direction: column;
}

/* Первое решение */.card__title {
    flex-grow: 1;
}

Кроме того, это возможно без использования flex-grow. Благодаря auto margins и flexbox! Я написал подробную статью об auto в CSS, если вы хотите вникнуть в подробности.

/* Второе решение */.card__date {
    margin-top: auto;
}

Сценарии использования — более одного фактора гибкости

Здесь я имею в виду использование flex-grow или flex-shrink, но со значением, отличным от 1. В этом разделе я рассмотрю некоторые идеи использования.

Вдохновленный Facebook (я тоже украл значки), этот нижний колонтитул (footer) действий состоит из четырех элементов, а последний имеет меньшую ширину. Мы можем сделать это следующим образом:

.actions {
    display: flex;
    flex-wrap: wrap;
}

.actions__item {
    flex: 2;
}

.actions__item.user {
    flex: 1;
}

Расширяющая анимацию

Мы можем сделать интересную вещь — анимировать flex элемент при наведении курсора. Это может быть чрезвычайно полезно. Вот простой пример этого:

.palette {
    display: flex;
    flex-wrap: wrap;
}

.palette__item {
    flex: 1;
    transition: flex 0.3s ease-out;
}

.palette__item:hover {
    flex: 4;
}

Расширение тарифного плана

Мне нравится демонстрация на Codepen. Как только card активирована, она расширяется, изменяя flex-grow на 4.

https://codepen.io/shshaw/pen/EbjvbQ

Когда контент больше, чем его оболочка

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

.wrapper {
    display: flex;
}

.wrapper img {
    flex: 1;
}

Однако даже с flex: 1 изображения все равно выходят за пределы оболочки. Согласно спецификации CSS:

По умолчанию flex элементы не сжимаются меньше своего минимального размера содержимого (длины самого длинного слова или элемента фиксированного размера). Чтобы изменить это, установите свойство min-width или min-height.

В нашем случае изображения слишком большие, и flexbox не сжимает их, чтобы поместиться в оболочку. Чтобы изменить это поведение, нам нужно установить следующее:

.wrapper img {
    flex: 1;
    min-width: 0;
}

Я объяснил эту проблему в своей статье о минимальной и максимальной width / height в CSS.

Заключение

Вот и все. Я объяснил основы свойств flex и объяснил, как их использовать. Надеюсь, вы узнали что-то новое из этой статьи. У вас есть комментарий или предложение? Пожалуйста, напишите мне на @shadeed9.

Спасибо за чтение.

Я пишу электронную книгу

Рад сообщить вам, что я пишу электронную книгу об Debugging CSS.

Если вам интересно, перейдите на debuggingcss.com  и подпишитесь на обновления о книге.

Ресурсы и дополнительная информация

Была ли вам полезна эта статья?
[18 / 4.9]

Spread the love
Editorial Team

View Comments

Recent Posts

Vue 3.4 Новая механика v-model компонента

Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование​ v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…

12 месяцев ago

Анонс Vue 3.4

Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…

12 месяцев ago

Как принудительно пере-отобразить (re-render) компонент Vue

Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…

2 года ago

Проблемы с установкой сертификата на nginix

Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…

2 года ago

Введение в JavaScript Temporal API

Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…

2 года ago

Когда и как выбирать между медиа запросами и контейнерными запросами

Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…

2 года ago