От автора: Около 6 месяцев назад меня попросили создать однострочную навигационную панель, которая будет растягиваться на всю ширину контейнера, но при этом число пунктов в ней может меняться. Количество пунктов меню было указано в другом месте и, скорее всего, могло поменяться в будущем.
Подумав наперед, я решил, что было бы нерационально изменять CSS всякий раз, когда потребуется добавить или удалить один из пунктов меню. Нужно было найти подходящее решение, и, в идеале, без применения JavaScript.
Задаем ширину в процентах
Если пункты меню будут фиксированной ширины, то они не смогут равномерно и полностью заполнить отзывчивый контейнер, как показано на следующем изображении.
Поэтому нам нужно использовать проценты, чтобы добиться нужного результата. Использование процентов — это распространенный способ создания резиновой сетки макета в отзывчивом дизайне, и он нам тоже здесь пригодится. Например, рассмотрим следующий привычный HTML-код:
1 2 3 4 5 6 7 8 9 10 |
<nav> <ul> <li>Главная</li> <li>О нас</li> <li>Услуги</li> <li>Продукция</li> <li>Вакансии</li> <li>Контакты</li> </ul> </nav> |
И мы добавим следующий CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
nav { width: 100%; background: #f0f0f0; border: 1px solid #ccc; border-right: none; } nav ul { overflow: hidden; margin: 0; padding: 0; } nav ul li { list-style: none; float: left; text-align: center; border-left: 1px solid #fff; border-right: 1px solid #ccc; width: 16.6667%; /* fallback for non-calc() browsers */ width: calc(100% / 6); box-sizing: border-box; } nav ul li:first-child { border-left: none; } nav ul li a { display: block; text-decoration: none; color: #616161; padding: 10px 0; } |
На что следует обратить внимание:
Элементу nav задано свойство width: 100%, чтобы он полностью заполнил все доступное пространство.
Элементам li заданы два свойства, отвечающие за ширину: width: calc(100% / 6); и width: 16.6667%;. Благодаря этому элементы меню разбиваются внутри элемента nav на 6 равных частей. Здесь важен порядок следования данных свойств, т.е. это позволит браузерам, поддерживающим свойство calc() использовать именно его вместо стандартного процентного значения для ширины. Браузеры, не поддерживающие свойство calc(), будут просто игнорировать его и использовать альтернативный вариант. Я привык использовать свойство calc() с процентами, чтобы повысить читаемость кода и позволить браузеру использовать оптимизированный результат. Таким образом, я уже не беспокоюсь об указании повторяющихся дробных значений.
Свойство box-sizing: border-box; задано для элементов li. Это заставляет свойство width включить значения свойств border-left и border-right в общее значение ширины. Без указания данного свойства элементы li не будут помещаться внутри области элемента nav, и последний элемент li будет перенесен на новую строку. Многие предпочитают назначать свойство box-sizing: border-box; для всех элементов, чтобы было легче управлять их шириной (рекомендация Пола Айриша (Paul Irish).
Результат добавления этих стилей показан на сайте CodePen:
Откройте его в новом окне и протестируйте на отзывчивость. Требуемый вид получен, и он является резиновым.
Что если мы добавим или уберем пункты меню?
Вот что произойдет, если мы уберем один из пунктов меню, оставив при этом прежний CSS-код:
Как видите, теперь справа, на месте удаленного пункта, образовалось пустое пространство. А что если вместо этого, наоборот, добавить еще один пункт меню?
Теперь последний пункт меню перескакивает на вторую строку. Очевидно, что все это не соответствует нашим требованиям. Для решения этой задачи можно использовать JavaScript, который посчитает число пунктов меню, определит их процентное соотношение и в цикле присвоит значения ширины для элементов li. Однако, я всегда неохотно использую JavaScript для отрисовки макета, если только нет никакого другого варианта. Более того, тогда бы это не была статья про CSS, если бы я собирался описать решение на JavaScript! Возвращаемся к решению нашей задачи…
(Новое) знакомство с табличным макетом
Не бойтесь, я не призываю вас возвращаться к использованию табличной HTML-верстки в духе 90-х. Конечно же, это было бы семантически неправильно. Однако, существуют значения свойства display, которые позволяют элементам вести себя, как таблицы. Это означает, что прежняя HTML-структура может быть использована вместе со следующим CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
nav { display: table; table-layout: fixed; width: 100%; } nav ul { display: table-row; margin: 0; padding: 0; } nav ul li { list-style: none; display: table-cell; text-align: center; } nav ul li a { display: block; } |
Устанавливая свойство display с нужным значением мы, по сути, превращаем элемент nav в таблицу, элемент ul – в строку, а элемент li – в ячейку таблицы. Обратите внимание, что мы также объявили свойство table-layout: fixed, которое задает равную ширину для каждого пункта меню. Его можно и удалить, но изменение длины текста у пунктов меню может привести к тому, что таблица будет выглядеть не выровненной, поэтому действуйте осторожно. Вот ссылка на демо-пример с кнопками, которые позволят вам добавлять/удалять пункты меню:
Возможно, это не самое идеальное решение (прошу прощения у ненавистников таблиц), зато это простое решение, которое работает. Браузерная поддержка тоже хорошая.
Будущее: модуль Flexbox
Модуль Flexbox является главным претендентом на то, чтобы заменить используемое здесь решение, когда старыми версиями браузера Internet Explorer перестанут пользоваться, и браузерная поддержка модуля Flexbox будет становиться все лучше и лучше. Если вы не знакомы с модулем Flexbox, то вы можете ознакомиться с недавно опубликованной статьей на сайте SitePoint или перейти к разделу со спецификацией, которая дает Flexbox следующее объяснение:
В модели «резинового» макета, дочерние элементы «резинового» контейнера могут быть расположены в любом направлении и, их размеры могут изменяться, либо в большую сторону, чтобы заполнить неиспользуемое пространство, или в меньшую сторону, чтобы избежать выхода за пределы родительского элемента. Также можно легко управлять как горизонтальным, так и вертикальным выравниванием дочерних элементов.
Звучит превосходно относительно решения нашей задачи. И снова мы можем использовать тот же HTML, а для реализации подхода Flexbox нам потребуется следующий CSS-код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
nav { width: 100%; } nav ul { display: flex; flex-direction: row; margin: 0; padding: 0; } nav ul li { list-style: none; flex-grow: 1; text-align: center; } nav ul li a { display: block; } |
Обратите внимание на следующие свойства Flexbox:
display: flex; – это свойство применяется к элементу ul и позволяет создать «гибкое» окружение для всех дочерних элементов.
flex-direction: row; – это свойство применяется к элементу ul для того чтобы дочерние элементы выстраивались слева направо. Правда, это значение по умолчанию, поэтому оно не так обязательно.
flex-grow: 1; – это магическое свойство, которое равномерно распределяет все элементы li внутри контейнера. Если бы вам нужно было задать конкретному элементу li значение 2, тогда этот элемент стал бы вдвое больше по сравнению с другими элементами.
У модуля Flexbox есть несколько интересных возможностей, которые могут быть полезными в других ситуациях. Например, возможность, которая позволяет переопределить порядок следования пунктов меню с помощью свойства order. Посмотрите конечный результат с применением Flexbox:
Как видите, мы добились такого же результата. И снова вы можете использовать кнопки, чтобы поэкспериментировать с добавлением/удалением одного пункта меню. Вы также можете добавить больше пунктов меню, изменив HTML-код. Изменяя ширину окна браузера, можно протестировать «резиновость» макета.
Автор: Dan Rose
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.