. Снова равномерное выравнивание блоков по ширине: постепенное улучшение до Flexbox
Снова равномерное выравнивание блоков по ширине: постепенное улучшение до Flexbox

Снова равномерное выравнивание блоков по ширине: постепенное улучшение до Flexbox

Задача равномерного выравнивания горизонтальных элементов (например, пунктов меню) по всей ширине контейнера стабильно остается актуальной в верстке. Два года назад Максим Усачев (psywalker) написал обстоятельнейший разбор ее решений, который заслуженно стал самой популярной статьей на CSS-live.ru. Были рассмотрены 4 варианта:

    (на базе float), к сожалению, не способный претендовать на универсальность; (в принципе, работоспособное решение, но только для фиксированной ширины элементов); и дополнительным элементом-распоркой (приемлемое решение); :after (лучшее решение).

У двух последних решений была изюминка в виде двух малоизвестных свойств CSS3 ( text-align-last и text-justify ), по иронии судьбы с незапамятных времен работающих в IE (где они и появились).

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

Разметка во всех примерах будет одна и та же — типичное горизонтальное меню на ul -списке.

1. Достижимый идеал

Теперь у нас есть механизм специально для раскладки макетов — Flexbox. С его помощью задача решается буквально двумя строчками кода:

Всё! В этих строчках — полное решение нашей задачи. Первая строчка превращает обычный контейнер в «волшебный» флекс-контейнер, а вторая включает равномерное распределение свободного пространства между пунктами, независимо от их ширины. Остальные свойства — фон, шрифт, отступы и т.п. — нужны буквально лишь для красоты.

Это решение работает сходу в Chrome 21+, Firefox 22+, Опере 12.1 (Presto) и 17+ (Blink), IE11+, а также актуальных мобильных версиях этих браузеров. Это более 60% по данным caniuse.com на момент написания статьи. Так что в новых проектах для «продвинутой» аудитории, а также в не особо важных местах, где равномерное растягивание важно для красоты, но не критично для функциональности, можете ограничиваться этими двумя строчками. В >60% случаев (дальше — больше) будет красиво, а в худшем случае меню станет из горизонтального вертикальным, но будет работать. Чем не изящная деградация?

2. Возвращение к реальности

Конечно, в реальном мире заказчик с дизайнером требуют, чтобы горизонтальное меню оставалось горизонтальным независимо от поддержки каким-то браузером какого-то свойства. Даже в IE8-10, из которых какое-то представление о флексбоксах есть только у 10-го. Но ведь у нас есть прекрасное готовое решение для IE со времен прошлой статьи! Почему бы не объединить его с «идеальным» решением?

Теперь у нас в современных браузерах работают флексбоксы, а в IE8-10 — text-align: justify для инлайн-блоков. А text-align-last избавляет нас от нужды в распорках (даже псевдо-). Кстати, работает оно не только в почти всех IE, но и в Firefox — аж с 12-й версии (правда, требует префикса -moz-).

Больше того, мы можем «в одно касание» добавить поддержку IE7 (и даже, в теории, IE5.5):

…вот только надо ли?:) В дальнейших примерах в статье мы обойдемся без хаков для IE7, лишь в финальном примере упомянем эту возможность.

3. Мобилизация резервов

Но старые IE — давно не главная проблема. Сегодня куда важнее мобильные браузеры — прежде всего iOS Safari и встроенный браузер Андроида. И как назло последний не поддерживает ни беспрефиксных флексбоксов, ни text-align-last .

Что же, возвращаться к распоркам? Ну уж нет! Все сколько-либо актуальные WebKit-браузеры поддерживают старую экспериментальную реализацию флексбоксов (спецификации 2009 года). И чем городить хаки, лучше уж подключить ее.

Пара свойств, включающих нужное нам отображение, в старых флексбоксах уже была и называлась так: display: box; box-pack: justify . На первый взгляд, достаточно добавить их контейнеру, с единственным нужным нам префиксом:

Но почему-то это не заработало. Оказывается, в отличие от новых флексбоксов, которые применяют свою «магию» к дочерним элементам флекс-контейнера независимо от их display , старые флексбоксы действовали только на блочное содержимое. И, поскольку нашим пунктам задан display:inline-block , с точки зрения старых флексбоксов они не стали самостоятельными флекс-элементами (flex-items), а оказались в единственном флекс-элементе, в который превратился окружавший их анонимный блочный бокс.

Итак, старые флексбоксы требуют, чтобы display:inline-block у элементов не было, а IE10- и Fx12-21 — чтобы был. Нам нужно «разрулить» этот конфликт. Я выбрал простейший путь — переопределил display для пунктов на тот же -webkit-box , т.е. сделал их блочными флекс-контейнерами «старого образца» исключительно в вебкитьей реализации:

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

Итак, мы заставили наше решение работать во всех десктопных браузерах и в подавляющей массе мобильных, всего один раз ступив на скользкую тропку хаков. Причем наш хак не использует неочевидных побочных эффектов, не полагается на магические числа, единственное, для чего он нужен — включить механизм, используемый нами по прямому назначению, там, где он работает. Даже в худшем случае пользователь увидит функциональное горизонтальное меню. А в наиболее вероятном — еще и красиво выровненное. Чем не постепенное улучшение?

Добавлено 11.04.15. Opera Mini больше не проблема! Обновление на ее серверах по волшебству добавило поддержку флексбоксов даже самым захудалым мобильникам, и теперь наше решение охватывает 93% всех браузеров (caniuse.com) даже без добавки старых IE!

4. Разгон и взлет

Для Safari (десктопных и мобильных) у нас пока работает старая версия флексбоксов, на быстродействие которых есть жалобы. Зато новые флексбоксы работают намного быстрее, и Safari их поддерживает — через префикс.

Всё, что нужно для перевода Safari на новые флексбоксы — еще раз продублировать пару свойств из «идеального» решения, но с префиксом -webkit:

Что ж, по сравнению с идеалом для 60% наше решение потеряло значительную часть изящества, но осталось в пределах одного экрана кода:) и только нужных селекторов, и по-прежнему не нуждается ни в каких распорках. И при этом работает во всех десктопных и всех мало-мальски серьезных мобильных браузерах! Теперь мы можем посмотреть в действии окончательный результат:

Уменьшая поддержку тех или иных браузеров, вы всегда сможете сделать код еще компактнее, убрав ненужные строки. А раскомментировав другие ненужные строки, можно осчастливить им даже последних динозавров. И совсем скоро для всех браузеров будет работать только чистое решение по последнему слову CSS-стандартов.

Кто сказал, что флексбоксы и постепенное улучшение несовместимы?

📎📎📎📎📎📎📎📎📎📎