Когда мы начинаем работать с адаптивным дизайном, мы сталкиваемся с различными техниками того, как лучше обработать изменение нашего навигационного меню для экранов с низким разрешением. Возможности кажутся бесконечными. Поэтому, я покажу вам четыре главных подхода с их достоинствами и недостатками. Три из них сделаны с использованием только CSS и один — с небольшим количеством JavaScript.
Введение
В коде, представленном в статье, я не использую браузерные префиксы, чтобы код стилей оставался простым к прочтению и пониманию. В более сложных примерах используется SCSS. Каждый из примеров размещен на сайте CodePen, где вы можете увидеть скомпилированный CSS.
Все подходы в этой статье используют простой HTML код, который я называю «базовое меню». Атрибут role используется чтобы указать определенный тип: горизонтальное меню (full-horizontal), выпадающий список (select), ниспадающее меню (custom-dropdown) и canvas.
<nav role="">
<ul>
<li><a href="#">Stream</a></li>
<li><a href="#">Lab</a></li>
<li><a href="#">Projects</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
Для стилей я использую один и тот же медиа запрос для всех вариантов:
@media screen and (max-width: 44em) {
}
1. Горизонтальное меню
Самый простой подход, потому что вам нужно лишь сделать список элементов шириной во всю страницу:
<nav role="full-horizontal">
<ul>
<li><a href="#">Stream</a></li>
<li><a href="#">Lab</a></li>
<li><a href="#">Projects</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
@media screen and (max-width: 44em) {
nav[role="full-horizontal"] {
ul > li {
width: 100%;
}
}
}
С дополнительным оформлением так оно выглядит на экранах с небольшим разрешением:
Преимущества
- Не требуется JavaScript
- Никакой лишней разметки
- Простой код стилей
Недостатки
- Занимает слишком много места на экране
Пример горизонтального меню можно увидеть на сайте CodePen.
2. Выпадающий список
В данном подходе скрывается базовое меню и показывается выпадающий список вместо него.
Чтобы добиться такого эффекта нам нужно добавить в базовую разметку выпадающий список. Чтобы он работал нам придется добавить JavaScript код, который изменяет значение window.location.href когда происходит событие onchange
<nav role="select">
<!-- basic menu goes here -->
<select onchange="if (this.value) window.location.href = this.value;">
<option value="#">Stream</option>
<option value="#">Lab</option>
<option value="#">Projects</option>
<option value="#">About</option>
<option value="#">Contact</option>
</select>
</nav>
Скрываем список на больших экранах:
nav[role="select"] {
> select {
display:none;
}
}
На маленьких экранах скрываем базовое меню и показываем выпадающий список. Чтобы помочь пользователю понять, что это меню — мы добавим псевдо-элемент с тектом «Меню»
@media screen and (max-width: 44em) {
nav[role="select"] {
ul {
display: none;
}
select {
display: block;
width: 100%;
}
&:after {
position: absolute;
content: "Menu";
right: 0;
bottom: -1em;
}
}
}
С дополнительным оформлением так оно выглядит на экранах с небольшим разрешением:
Преимущества
- Не занимает много места
- Использует «собственные» элементы управления
Недостатки
- Для работы требуется JavaScript
- Происходит дублирование содержимого
- Выпадающий список не удается стилизовать во всех браузерах
3. Пользовательское ниспадающее меню
В данном подходе на небольших экранах скрывается базовое меню и показывается input и label вместо них (используется хак с чекбоксом). Когда пользователь кликает на label, базовое меню показывается под ним.
<nav role="custom-dropdown">
<!-- Advanced Checkbox Hack (see description below) -->
<!-- basic menu goes here -->
</nav>
Проблемы с использованием хака с чекбоксом
Две основных проблемы с этим решением:
- Оно не работает на мобильных версиях Safari (iOS < 6.0). Невозможно кликнуть на label в браузере под iOS < 6.0, чтобы сработал input из-за бага. Решается добавлением пустого события onclick на label
- Оно не работает на основном браузере ОС Android версии меньше или равной 4.1.2. Давным давно был баг в WebKit движке, который не позволял использовать псевдо-классы с комбинацией селекторов
+
и~
h1 ~ p { color: black; }
h1:hover ~ p { color: red; }
Это не оказывало никакого эффекта, потому что хак с чекбоксом использовал псевдокласс :checked
с селектором ~
. И пока баг не был исправлен в WebKit 535.1 (Chrome 13) и в актуальном для Android 4.1.2 WebKit 534.30, хак не работал ни на каком устройстве с ОС Android.
Лучшее решение — это использовать анимацию только для WebKit-браузеров для тега <body>
Комбинация всех вариантов создает расширенный хак для чекбоксов:
<!-- Fix for iOS -->
<input type="checkbox" id="menu">
<label for="menu" onclick></label>
/* Fix for Android */
body {
-webkit-animation: bugfix infinite 1s;
}
@-webkit-keyframes bugfix {
from { padding: 0; }
to { padding: 0; }
}
/* default checkbox */
input[type=checkbox] {
position: absolute;
top: -9999px;
left: -9999px;
}
label {
cursor: pointer;
user-select: none;
}
Для больших экранов мы скрываем label:
nav[role="custom-dropdown"] {
label {
display: none;
}
}
Для небольших экранов мы скрываем базовое меню и показываем label. Чтобы помочь пользователю понять, чтобы это меню, мы добавим псевдоэлемент с текстом "≡" в label (представим в виде кода "2261", чтобы использовать как содержимое псевдоэлемента). Когда пользователь кликает на input, базовое меню показывается и список элементов раскрывается во всю ширину.
@media screen and (max-width: 44em) {
nav[role="custom-dropdown"] {
ul {
display: none;
height: 100%;
}
label {
position: relative;
display: block;
width: 100%;
}
label:after {
position: absolute;
content: "2261";
}
input:checked ~ ul {
display: block;
> li {
width: 100%;
}
}
}
}
Так меню выглядит на маленьких экранах:
Преимущества
- Не занимает много места в закрытом состоянии
- Целиком стилизуется
- Не требует JavaScript
Недостатки
- Не семантичный код (input / label)
- Требуется дополнительный HTML
4. Canvas
В этом подходе, на небольших экранах, скрывается базовое меню и показывается input и label как в варианте 3. Когда пользователь кликает на label, базовое меню выплывает слева и содержимое перемещается вправо. Экран разделяется на части в пропорциях 80% меню и 20% содержимое (в зависимости от разрешения и единиц, используемых в CSS)
<input type="checkbox" id="menu">
<label for="menu" onclick></label>
<!-- basic menu goes here -->
<div class="content">
<!-- content goes here -->
</div>
На больших экранах мы скрываем label.
label {
position: absolute;
left: 0;
display: none;
}
На маленьких экранах мы помешаем меню вне содержимого окна и показываем label и input. Чтобы скрыть меню мы устанавливаем для него ширину и отрицательное значение положения. Чтобы помочь пользователю понять, чтобы это меню, мы так же добавим псевдоэлемент с текстом "≡" в label (в виде кода "2261", чтобы использовать как содержимое псевдоэлемента).
@media screen and (max-width: 44em) {
$menu_width: 20em;
body {
overflow-x: hidden;
}
nav[role="off-canvas"] {
position: absolute;
left: -$menu_width;
width: $menu_width;
ul > li {
width: 100%;
}
}
label {
display: block;
}
label:after {
position: absolute;
content: "2261";
}
input:checked ~ nav[role="off-canvas"] {
left: 0;
}
input:checked ~ .content {
margin-left: $menu_width + .5em;
margin-right: -($menu_width + .5em);
}
}
С дополнительным оформлением так оно выглядит на экранах с небольшим разрешением:
Преимущества
- Не занимает много места в закрытом состоянии
- Целиком стилизуется
- Не требует JavaScript
- Работает как Facebook / Google+ приложения
Недостатки
- Не семантичный код (input / label)
- Требуется дополнительный HTML
- Абсолютное позиционирование элемента body вызывает ощущение зафиксированного положения
Работает ли это под IE?
Все использованные техники преследуют одну цель: создать адаптивное меню для современных браузеров! И все потому, что нет никаких IE8 или ниже ни на каких мобильных устройствах и, поэтому, мы можем совершенно не беспокоиться об этом вопросе.
Автор: druf