Line Menu Icon… Это на самом деле меню

У меня было довольно смутное представление о нем в ту ночь, когда я его сделал. Я просто писал код (у вас тоже такое бывает, правда?)

Вы видели эти маленькие иконки, через которые можно вывести меню навигации? Между собой мы называем их Three Line Menu, но многим они известны под названиями NaviCon (серьезное) или Hamburger (прикольное).

Изображение на иконках выглядят как маленькое меню, так что, надеюсь, это очевидно, что при клике на ней, вам выводится реальное меню. Но что, если эта иконка и не икона вовсе? А реальное меню, просто миниатюрное?

Концепция

Согласен, идея немного сумасшедшая.

Концепция

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

По сути процесс включает в себя:

  • Сначала создаем полноразмерную версию;
  • Через трансформации уменьшаем элементы до размера иконок: scale(0.X);
  • Удаляем лишние биты через opacity: 0;
  • Когда элемент неактивен и выводится в виде иконки, вся область элемента реагирует на клик;
  • При клике по элементу выводится полнорамерная версия через transform: scale(1), скрытые биты — через opacity: 1, она может работать, как обычное меню.

Вот элементы дизайна:

элементы дизайна

Переключение режимов

Переключать «режимы» (полноразмерный или уменьшенный) можно несколькими способами. Через checkbox hack или совсем просто через JQuery:

$(".main-nav").on("click", function() {
  $(this).toggleClass("open");
  return false;
});

Все, что мы делаем здесь, это просто меняем класс, что является проверенным способом.

CSS

Для изменения режимов лично я предпочитаю использовать Sass. Он позволяет задавать разные режимы внутри одного и того же блока:

.main-nav {
  /* Анимация при смене режима */
  transition: 0.3s ease;
  transform: 
    /* Make mini */
    scale(0.1) 
    /* 10-кратное изменение размеров */
    translateZ(0);
  /* Полноразмерный режим */
  &.open {
    transform: scale(1.0);
  }
}

Обработка кликов

С кликами вообще интересная история.

Нам нужно, чтобы:

  • При нажатии на иконку открывалось полноразмерное меню;
  • В полноразмерной версии все пункты меню при нажатии работали, как обычное меню;
  • Существовала возможность свернуть меню.

У нас уже используется JQuery в месте, где происходит переключение классов во время клика по иконке. Можно все так и оставить.

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

Все эти ссылки в меню представляют собой список <ul>, поэтому один из способов решить данную проблему заключается в том, чтобы использовать CSS, в котором по умолчанию эти элементы не активны (в миниатюрной версии), но в полноразмерном режиме по ним можно кликать.

.main-nav {
  ul {
    pointer-events: none;
  }
  &.open {
    ul {
      pointer-events: auto;
    }
  }
}

Удивительные возможности открывает pointer-events, однако он перестал поддерживаться в IE 10 (в IE 11 поддержка снова реализована).

Также вы можете вместо него использовать дополнительный <div> , который задействуется для описания объекта в режиме по умолчанию (миниатюрная версия), и просто исключается, когда описывается полноразмерное меню.

Как закрыть меню

Обеспечить сворачивание меню можно, просто добавив кнопку:

<button class="close-button">
  <b class="visually-hidden">Close</b>
  <span aria-hidden="true">
    ×
  </span>
</button>

Мы хотели использовать для этого значок ;, который сам по себе является иконкой. Но вы можете не понять его смысл, когда смотрите на меню (или можете понять слишком «расплывчато»).

Поэтому мы убрали его, заменив более понятным словом, которое в миниатюрном режиме скрыто (выводится в развернутом режиме с помощью позиционирования). Стандартная процедура для иконок.

В нашем простом примере нам даже не нужно было привязывать обработчик событий непосредственно к этой кнопке. У нас уже есть один, прикрепленный ко всему родительскому элементу обработчик, который переключает меню в полноразмерный режим.

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

Семантика?

На самом деле использование не то чтобы однозначно восхитительное решение, мы потратили кучу времени в бесконечных спорах по этому поводу. Нет смысла применять его без JS, так что, вставлять его с JS? Должны ли мы сделать его анкорной ссылкой, ведущей обратно к началу страницы? Будет ли <button> тогда корректным элементом?

Прогрессивное улучшение?

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

Но думаю, что наш код итак уже не плох. Применение <button> для кнопки закрытия меню — нормальное решение. Нет необходимости вставлять ссылку, которая потянет за собой необходимость применения JS.

И соответственно будет работать только с функционалом JS. А так мы придумали простое решение, которое выглядит следующим образом:

$('<button class="close-button">
    <b class="visually-hidden">Close</b>
    <span aria-hidden="true">
      ×
    </span>
  </button>').appendTo(".main-nav");

Еще одной проблемой является то, что мы построили CSS таким образом, что режим по умолчанию — это закрытое меню. И этот «закрытый» режим в значительной степени опирается на CSS-преобразования, сворачивающие его.

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

Если бы наше меню не перекрывало содержание страницы, тогда еще ладно. Но так как оно на самом деле его перекрывает, нам пришлось серьезно поломать голову над тем, как реализовать наши идеи, если браузер не поддерживает CSS.

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

Что мы можем взять из него, так это имя класса, которое указывает на то, что мы имеем дело с <html> элементом. Мы можем использовать это, чтобы воплотить в реальность все свои фантазии.

.main-nav {
  /* Стиль оп умолчанию свернутого режима */
}
.csstransforms .main-nav {
  /* Вносим немного фантазии */
}

IE 8 не поддерживает преобразования CSS, поэтому:

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

html:

<nav role='navigation' class="main-nav">
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Clients</a></li>
    <li><a href="#">Contact Us</a></li>
  </ul>
</nav>

<h1>Line Menu Icon OR IS IT?!</h1>
  
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nihil eum ipsam sit a porro velit ratione recusandae rem maiores ullam minima assumenda quas quibusdam aperiam voluptas veniam architecto accusantium neque sequi fugit enim tempora voluptates. Aperiam ut animi quos delectus ullam vel impedit debitis necessitatibus!</p>

scss:

@import url(http://fonts.googleapis.com/css?family=Boogaloo);

body {
  background: #F06D06;
  padding: 2rem 4rem 2rem 8rem;
  color: white;
  font-family: "Lucida Grande", sans-serif;
  font-size: 0.9rem;
  line-height: 1.4;
  overflow: scroll;
}

.main-nav {
  ul {
    list-style: none;
    padding: 0;
    margin: 0;
  }
  li {
    display: inline-block;
    padding: 5px;
  }
  a {
    color: white;
    text-decoration: none;
  }
  .close-button {
    display: none;
  }
}

.csstransforms .main-nav {
  position: absolute;
  top: 2.4rem;
  left: 2rem;
  width: 20rem;
  border: solid 1rem white;
  background: rgba(#F06D06, 0.75);
  box-shadow: 0 0 5px rgba(black, 0.2);
  padding: 4rem;
  transition: 0.3s ease;
  transform: scale(0.11) translateZ(0);
  transform-origin: top left;
  cursor: pointer;
  ul {
    list-style: none;
    margin: 0;
    padding: 0;
    pointer-events: none;
  }
  a {
    font-family: 'Boogaloo', cursive;
    font-size: 1.6rem;
    text-transform: uppercase;
    display: block;
    margin: 0 0 2rem 0;
    padding: 0.5rem;
    color: transparent;
    &:hover, &:focus {
      background: black;
      color: white !important;
    }
  }
  li {
    background: white;
    display: block;
    padding: 0;
  }
  li:last-child a {
    margin: 0;
  }
  &.open {
    transform: scale(1.0);
    padding: 2rem;
    ul {
      pointer-events: auto;
    }
    a {
      color: black;
    }
    .close-button {
      opacity: 1;
    }
  }
  .close-button {
    display: block;
    position: absolute;
    top: -2rem;
    left: -2rem;
    border: 0.5rem white solid;
    border-radius: 50%;
    background: #F06D06;
    text-align: center;
    width: 4rem;
    height: 4rem;
    color: white;
    font-weight: bold;
    font-size: 2rem;
    opacity: 0;
    transition: opacity 0.2s ease;
    outline: 0;
    span {
      position: relative;
      top: -3px;
    }
    &:hover, &:active {
      background: black; 
    }
  }
}

h1 {
  font-family: 'Boogaloo', cursive;
  text-transform: uppercase;
  padding: 0;
  margin: 0;
  font-size: 3rem;
}

.visually-hidden {
  position: absolute;
  top: -9999px;
  left: -9999px;
}

js:

$('<button class="close-button">
    <b class="visually-hidden">Close</b>
    <span aria-hidden="true">
      ×
    </span>
  </button>').appendTo(".main-nav");

$(".main-nav").on("click", function() {
   $(this).toggleClass("open");
  return false;
});

А по-другому?

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

Это что-то вроде анимации, которая делает элемент более понятным. И в принципе это главный аргумент использовать данный вариант – с помощью анимации сделать элемент более понятным. Например:

html:

<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam.</p>

<div class="popup-info">
  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/icon_7455.svg" class="popup-icon">
  <p>More information and stuff. Very important. Must read.</p>
</div>

</section>  

<section>
  
  <p>Saepe provident nesciunt fuga quae natus doloribus exercitationem quam quod cumque quia tempore unde omnis quibusdam magnam non aliquid inventore aperiam. Dolorum explicabo totam unde voluptates iure itaque veniam porro.</p>

<div class="popup-info">
  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/icon_7455.svg" class="popup-icon">
  <p>More information and stuff. Very important. Must read.</p>
</div>

</section>

scss:

@import url(http://fonts.googleapis.com/css?family=Alegreya+Sans);
* { box-sizing: border-box; }
body {
  font-family: 'Alegreya Sans', sans-serif;
  width: 200px;
  margin: 20px auto;
}
section {
  position: relative;
}

.popup-info {
  width: 200px; 
  color: white;
  text-align: center;
  padding: 1rem;
  max-height: 0;
  position: absolute;
  top: 0;
  left: 100%;
  transition: 0.3s;
  overflow: hidden;
  > .popup-icon {
    position: absolute;
    top: 0;
    left: 0;
    opacity: 1;
    width: 20px;
    transition: 0.3s;
  }
  p {
    display: none;
  }
  &.open {
    background: #F06D06;
    position: absolute;
    max-height: 300px;
    padding-top: 70px;
    z-index: 1;
    .popup-icon {
      top: 15px;
      left: 50%;
      width: 40px;
      margin-left: -20px;
    }
    p {
      display: block;
    }
  }
}

js:

$(".popup-info").on("click", function() {
  $(this).toggleClass("open");
  return false;
});

Перевод статьи «Line Menu Icon… That Is A Menu» был подготовлен дружной командой проекта Сайтостроение от А до Я.