Pull to refresh

Подходы к реализации адаптивного меню

Reading time6 min
Views57K
Original author: Tim Pietrusky
Когда мы начинаем работать с адаптивным дизайном, мы сталкиваемся с различными техниками того, как лучше обработать изменение нашего навигационного меню для экранов с низким разрешением. Возможности кажутся бесконечными. Поэтому, я покажу вам четыре главных подхода с их достоинствами и недостатками. Три из них сделаны с использованием только CSS и один — с небольшим количеством JavaScript.

image

Введение


В коде, представленном в статье, я не использую браузерные префиксы, чтобы код стилей оставался простым к прочтению и пониманию. В более сложных примерах используется 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>

Проблемы с использованием хака с чекбоксом

Две основных проблемы с этим решением:
  1. Оно не работает на мобильных версиях Safari (iOS < 6.0). Невозможно кликнуть на label в браузере под iOS < 6.0, чтобы сработал input из-за бага. Решается добавлением пустого события onclick на label
  2. Оно не работает на основном браузере ОС 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-браузеров для тега
.

Комбинация всех вариантов создает расширенный хак для чекбоксов:

<!-- 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 или ниже ни на каких мобильных устройствах и, поэтому, мы можем совершенно не беспокоиться об этом вопросе.
Tags:
Hubs:
Total votes 24: ↑21 and ↓3+18
Comments5

Articles