18.10.2021
6bc2d3f51c29c521e8e1efab0646d253

Одинаковые столбцы с помощью Flexbox: это сложнее, чем вы думаете

Однообразные столбцы с помощью Flexbox: это труднее, чем вы думаете

От создателя: вы имеете красивый дизайн, в котором есть прекрасный большой hero-раздел, за которым следует секция из этих 3-х столбцов прямо под ним. Как и практически любой другой веб-сайт, над которым вы когда-либо работали.

Схожие столбцы с помощью Flexbox: это труднее, чем вы думаете

Вы проходите через hero-раздел и приступаете к работе над разделом из 3-х столбцов. Пора вспомнить о нашем ненадежном товарище flexbox! Но, вы пишете display:flex и получаете кавардак вместо ожидаемых трех одинаковых столбцов.

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

Я уверен, что вы лицезрели примеры и учебные пособия, в которых рассматривается что-то вроде того, где три div сжимаются вокруг содержимого, снутри них:

Другими словами, мы получаем некие элементы блочного уровня, которые сжимаются и располагаются рядом друг с другом. Такое чувство, что flex хочет, чтобы все было как можно меньше. Но на самом деле flexbox вправду хочет, чтобы все было как можно больше.

Подождите, почему? Flex по умолчанию сжимает объекты — такового не может быть! Верно?

Каким бы потрясающим ни был flexbox, то, что он делает под капотом, на самом деле малость странно, потому что по умолчанию он делает две вещи одновременно. Сначала он глядит на размер содержимого, который мы получили бы, объявив в элементе width:max-content. Но кроме этого, flex-shrink также проделывает некоторую работу по уменьшению размеров частей, но только при необходимости.

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

Погружение в max-content

max-content — достаточно удобное свойство в определенных ситуациях, но мы желаем понять, как оно работает в данной определенной ситуации, когда мы пытаемся получить три равных столбца. Итак, давайте на мгновение отбросим flexbox и поглядим, как max-content работает сам по для себя.

MDN хорошо объясняет это: Ключевое слово max-content представляет внутреннюю ширину наибольшего содержания. Для текстового содержимого это значит, что содержимое вообще не будет переноситься, даже если оно вызывает переполнение.

Intrinsic может сбить вас с толку, но в основном это значит, что мы позволяем контенту определять ширину, а не устанавливаем заданную ширину очевидно. Ури Шакед отлично описывает такое поведение, говоря: «браузер делает вид, что у него нескончаемое пространство, и помещает весь текст в одну строчку, измеряя ее ширину».

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

Давайте вернемся к этому черезмерно облегченному примеру трех элементов блочного уровня, которые сжимаются и располагаются рядом друг с другом. Этого происходит поэтому, что размер этих элементов равен их заявленной ширине max-content. Ширина блочного элемента практически такая же, как и общая ширина объединенного контента снутри каждого элемента.

Взгляните на эти элементы без flexbox, но заместо этого со свойством width:max-content:

Итак, когда текстов очень не достаточно, сжимает элементы внутренняя функция max-content, а не flex-shrink. Естественно, flexbox также имеет поведение по умолчанию flex-direction:row, которое превращает flex-элементы в столбцы, помещая их рядом друг с другом. Вот еще один пример, но с выделенным свободным местом.

Добавление flex-shrink

Итак, мы лицезреем, что объявление display:flex подталкивает внутренний размер max-content к flex-элементам. Эти элементы желают быть такими же большими, как и их содержание. Но есть и другое значение по умолчанию, которое есть в flex-shrink.

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

Но если flex-элементы вправду переполняют контейнер (благодаря внутренней ширине max-content), flex-элементы имеют возможность сжиматься, чтобы предотвратить это переполнение, поэтому что flex-shrink это отслеживает.

Вот почему по умолчанию для flex-shrink установлено значение 1. Без него все очень стремительно запуталось бы.

Вот почему столбцы не равны

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

Схожие столбцы с помощью Flexbox: это труднее, чем вы думаете

Используя DevTools Firefox (у него есть несколько уникальных визуализаций flexbox, которых нет у других браузерах), мы можем узреть, как flexbox вычисляет значения.

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

В приведенном ниже CodePen у нас есть родительский элемент, представляющий собой контейнер (display:flex) шириной 600px. Я удалил все, что могло воздействовать на цифры, так что нет gap либо padding. Я также отключил border, чтоб мы могли легко визуализировать результат.

1-ый и третий flex-элементы имеют width:300px а средний — width:600px. Если мы сложим все это, всего получится 1200px. Это больше, чем доступно в родительском элементе 600px, потому начинает работать flex-shrink.

Flex-shrink это соотношение. Если все элементы имеют однообразный flex-shrink (по умолчанию равный 1), они сжимаются с схожим соотношением. Это не означает, что все они сжимаются до 1-го и того же размера или на схожую величину, но все они сжимаются с схожим соотношением.

Если мы вернемся в Firefox DevTools, мы увидим базисный размер flex-shrinkа и окончательный размер. В данном случае два 300px элемента на данный момент имеют ширину 150px, а один 600px — на данный момент равен 300px.

Схожие столбцы с помощью Flexbox: это труднее, чем вы думаете

Однообразные столбцы с помощью Flexbox: это труднее, чем вы думаете

Если мы сложим базисные размеры всех трех flex-элементов (фактическую ширину, которую мы для их объявили), общая сумма будет равна 1200px. Ширина нашего flex-контейнера равна 600px. Если поделить все на 2, все сходится! Все элементы уменьшаются с схожим соотношением, и их ширина делится на 2.

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

Уравнивание столбцов

Есть несколько различных способов получить три столбца, которые должны быть схожей ширины, но некоторые из них лучше, чем другие. Для всех подходов основная мысль состоит в том, что мы желаем, чтобы базовый размер всех столбцов был схожим . Если у них одинаковый базовый размер, они будут сжиматься (либо расти, если мы используем flex-grow) в схожем соотношении, и теоретически это должно сделать их схожего размера.

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

Способ 1: Использование flex:1

Один из методов добиться того, чтобы все flex-элементы имели однообразный базовый размер, — это объявить для всех flex:1:

CSS

На собственных лекциях я использовал другой подход, и у меня, должно быть, было 100 человек, которые спрашивали, почему я не использую flex:1 заместо этого. Я ответил, что не желаю углубляться в flex-стенографию. Затем я использовал новейшую демонстрацию с flex:1 и, к моему удивлению, это не сработало. Столбцы не были равны.

Средний столбец тут больше двух других. Так почему же это не сработало? Виной всему свойство компонента padding в среднем столбце.

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

Когда я в первый раз увидел эту всплывающую проблему, мне захотелось выяснить, что на самом деле происходит. Сокращенная flex делает больше, чем просто установает flex-grow:1. Если вы ещё не понимаете, flex это сокращение flex-grow, flex-shrink и flex-basis. Значения по умолчанию для этих составляющих параметров:

CSS

Я не собираюсь углубляться в flex-basis в данной статье. На сегодняшний момент мы можем думать об этом как о width, для упрощения, так как мы не работаем с flex-direction.

Мы уже лицезрели, как работает flex-shrink. Flex-grow наоборот. Если размер всех гибких частей вдоль главной оси меньше, чем у родительского элемента, они имеют возможность увеличиваться, чтобы заполнить это пространство. Итак, по умолчанию flex-элементы:

не вырастают;

если они переполняют родительский элемент, они имеют возможность сжиматся;

их ширина действует как max-content.

Собирая все совместно, flex по умолчанию:

CSS

Самое увлекательное в flex сокращении — это то, что вы сможете опускать значения. Так что, когда мы объявляем flex:1, это установка первого значения, flex-grow равным 1.

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

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

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

Но если у нас есть три div с неравной шириной — как те, с которых мы начали, — добавление flex-grow к ним вообщем ничего не даст. Они не будут расти, поэтому что они уже занимают все доступное место – и на самом деле, их необходимо сжать с помощью flex-shrink, чтобы они уместились!

Но, как мне произнесли, настройка flex:1 может работать для сотворения одинаковых столбцов. Ну вроде как мы лицезрели выше! В простых ситуациях это вправду работает, как вы можете видеть ниже.

Когда мы объявляем, что flex:1 это работает, поэтому что это не только устанавливает значение flex-grow равным 1, но также изменяет значение flex-basis!

CSS

Настройка flex:1 устанавливает flex-basis значение 0%. Это перезаписывает тот внутренний размер, который у нас был до того, как мы вели max-content. Все наши flex- элементы сейчас хотят иметь базовый размер 0!

Схожие столбцы с помощью Flexbox: это труднее, чем вы думаете

Смотря на расширенное представление flex:1 в DevTools, мы лицезреем, что значение flex-basis изменилось на 0

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

Схожие столбцы с помощью Flexbox: это труднее, чем вы думаете

DevTools FireFox указывает, что элемент flex:1 имеет размер содержимого 0 и возрастает, чтобы заполнить доступное пространство.

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

Но, как мы лицезрели, это не всегда так …

Причина этого в том, что когда flexbox делает все это и распределяет пространство, независимо от того, сжимается ли либо растет flex-элемент, он смотрит на размер содержимого элемента. Если вы вернетесь обратно к блочной модели, мы имеем размер самого контента, потом padding, border и margin вне этого.

И нет, я не запамятовал * { box-sizing: border-box; }.

Это одна из странноватых причуд CSS, но она имеет смысл. Если бы браузер смотрел на ширину этих частей и включал padding и borders в вычисления, как он мог бы уменьшить их? Или padding должен будет сжиматься, либо что-то переполнит место. Обе эти ситуации ужасны, поэтому заместо того, чтобы смотреть на значение частей box-size при вычислении их базового размера, он глядит только на поле содержимого!

Итак, настройка flex:1 вызывает делему в тех случаях, когда некоторые из частей содержат borders или padding. Теперь у меня есть три элемента с размером содержимого 0, но у среднего из их есть отступы. Если бы у нас не было этого отступа, у нас была бы та же математика, что и при рассмотрении того, как работает flex-shrink.

Родительский элемент шириной 600px и три flex-элемента шириной 0px. У всех их есть flex-grow:1, поэтому они вырастают с одинаковым соотношением, и каждый из их становится шириной 200px. Но padding все портит . Заместо этого я получаю три div с размером содержимого 0, но у среднего есть padding:1rem. Это значит, что он имеет размер содержимого 0 плюс отступы 32px.

Мы должны разделять поровну 600 — 32 = 568px, а не 600px. Все div желают расти с одинаковым соотношением, поэтому 568 / 3 = 189.3333px.

И вот что происходит!

Но … помните, это размер их содержимого, а не общая ширина! Остается два div с шириной 189.333px и еще один div с шириной 189.333px + 32 = 221.333px. Это достаточно существенная разница!

Метод 2: flex-basis: 100%

Я всегда поступал так:

CSS

Фактически, это должно было быть моим окончательным решением после того, как я показал, что flex:1 не работает. Но пока я писал эту статью, я сообразил, что это та же проблема, но еще менее очевидна. Достаточно, чтобы ее не сходу заметить.

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

Дело в том, что в вычисленные значения 100% врубаются отступы (из-за * { box-size: border-box; }которые, я предполагаю, что все употребляют). Итак, внешние блоки div имеют размер содержимого 600px, тогда как средний div имеет размер содержимого 600 — 32 = 568px.

Как умеренно распределить пространство, учитывая что делить необходимо 1768px на 600px а не 1800px. Не считая того, как мы видели ранее, flex-элементы сжимаются не в схожей степени, а с одинаковсоотношением! Таким образом, элемент с padding сжимается незначительно меньше, чем другие.

Это приводит к тому, что .card имеет ширину в 214.483px в то время как ширина других равна 192.75px. Снова же, это приводит к неравным значениям ширины, несмотря на то что разница меньше, чем мы видели с решением при flex:1.

Почему CSS Grid – это наилучший выбор

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

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

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

Обычно, когда мы используем сетку, мы очевидно объявляем наши столбцы с помощью grid-template-columns. Но мы не обязаны этого делать. Мы можем вынудить столбцы вести себя так же, как flexbox, используя grid-auto-flow:column.

CSS

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

CSS

Мне нравится, что все это находится в родительском селекторе и что нам не необходимо выбирать дочерние элементы, чтобы получить макет, подходящий нам макет!

Тут интересно то, как работают юниты fr. В спецификации они практически называются «flex units», и они работают так же, как и flexbox при разделении места. Большая разница: они смотрят на другие столбцы, чтоб определить, сколько у них места, не обращая внимания на содержимое снутри столбцов.

Это настоящая мистика. Объявляя grid-auto-columns:1fr, мы, по сущности, говорим: «По умолчанию все мои столбцы должны иметь схожую ширину», чего мы и добивались с самого начала!

А как насчет малеханьких экранов?

Что мне нравится в этом подходе, так это то, что мы можем сделать его очень обычным:

CSS

И вот так просто – все работает отлично. Объявив display:grid с самого начала, мы можем включить, gap чтоб сохранить равный интервал, независимо от того, являются ли дочерние элементы строчками или столбцами.

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

И, естественно же, если вы предпочитаете код без медиа-запросов, ничто не мешает вам перейти на последующий уровень с помощью auto-fit, что было бы похоже на настройку чего-то вроди flex-wrap (несмотря на то что и не совсем то же самое):

CSS

Заставляем код работать с Flexbox

Я понимаю, что мы можем обойти это с помощью flexbox, вложив в него элемент padding. Мы делали так с тех пор, как начали создавать макеты с внедрением float, и Bootstrap действительно внедрил этот тип шаблона проектирования.

И в этом нет ничего отвратительного. Оно работает! Но float тоже работает, а мы не стали использовать их для макетов, так как в последние годы были выпущены наилучшие решения.

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

Естественно, Flexbox все еще актуален

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

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

Но в других ситуациях, когда вы обнаруживаете, что боретесь с неуввязками flexbox, возможно, вы могли бы заместо этого обратиться к сетке, даже если это не обычная «2d» сетка.

Люди нередко говорят мне, что им сложно осознать сетку, потому что она слишком сложна, и несмотря на то что, возможно, иногда это правда, но как мы лицезрели здесь, это не всегда так.

По материалам webformyself.com

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *