Доброго времени суток. Сегодня разберем создание “двухуровневого” меню. Нам пригодятся знания, полученные из первой части.

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

В статье Все статьи

Начинаем, как обычно с разметки

HTML

Как вы можете заметить 2-ой уровень лежит на том же уровне что и <a>. В интернете Вы можете найти более экзотические варианты (обычно это в старых статьях). Сейчас такая разметка используется де-факто, поэтому и я буду придерживаться ее.

И так, 1-ый уровень мы можем оформить 3 вариантами, 2-ой тоже :). Итого у нас появляется комбинация минимум из 9 вариантов. Какой использовать решать Вам. Способ оформления для 1-ого уровня мы уже определили (float:left). Переходим к вариантам 2-го:

  1. left:-9999px
  2. display:none;
  3. visible:none + opacity: 0

left:-9999px
(example_1::github)

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

Суть его заключается в том, что 2-ому уровню (li > ul) присваивается свойство position:absolute (это свойство используется во всех случаях, т.к. необходимо, чтобы 2-ой уровень был выдернут из основного потока контекста) и закидывается за границы экрана, т.е. left:-9999px (с тем же успехом можно использовать и right/top:-9999px, или bottom:9999px).

А при наведении на родительский li (li:hover > ul), присвоить left:0 таким образом меню вернется на свое место. Примерно так.

Теоретически все классно, но если вы так сделаете, то появится одна проблемка: все меню 2-го уровня будут появлятся в левом верхнем углу экрана. А случилось это потому, что относительные координаты

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

Или первый ближайший предок с position:relative

Т.е. нам надо присвоить position:relative первому уровню (.menu_top > ul > li)

А если мы это сделаем, то и ширина всего 2-го уровня будет наследоваться от элемента 1-го уровня. И если мы захотим ее изменить, то придется задавать min-width или width.

Короче куча проблем.

CSS

display:none
(example_2::github)

Все можно сделать проще и без танцев с бубном. Для этого вместо left:-9999px; мы указываем display:none, а вместо left:0 -> display:block. И все

Вроде бы этот метод лишен всех предыдущих проблем (и это действительно так), но он тянет свои собственные. Обратите внимание, где появляется 2-ой уровень. Т.к. он находится на одном уровне с <a>, то и получает, что на него действуют все padding предназначенные для li. И тут открывается целое поле для творчества. Можно:

  1. Оставить все как есть
  2. Не использовать padding
  3. margin-left: -50% (CSS2.1). В этом случае меню 2-ого уровня будет прилеплено к границе родительского элемента.
  4. transform: translateX(-50%). А это свойство позволяет нам отцентровать меню под родителем.

А вот как это выглядит в реальности

Комбинация
(example_3::github)

Думаю многих заинтересует как сделать так, чтобы каждый пункт меню писался в 1 строку. Т.к. если мы используем процентные margin, left приходится задавать position:relative для родителей, что в свою очередь влияет на width дочерних элементов. Я нашел следующий способ: свойство white-space: nowrap;

P.S. заметьте, эти решения используются исключительно в тех случаях, если вы не хотите задавать фиксированную ширину меню. Последнии 2 способа имеют смысл если вы не задаете фиксированную ширину подменю.

visibility:hidden + opacity: 0
(example_4::github)

По большому счету можно использовать visibility как еще один способ, но смысла в этом нет. Т.к. при условии, что мы используем position:absolute разницы с display:none не будет (только уменьшение производительности, за счет того, что меню остается в контекста и браузер будет рассчитывать его). Но у visibility есть одно существенное преимущество. Его можно использовать вместе со свойством opacity. Оно необходимо, в том случае, если Вы хотите использовать CSS-анимацию появления:

  1. opacity:0 – меню полностью прозрачное, но на него до сих пор можно нажать.
  2. visibility:hidden – прячем меню (теперь на него нажать нельзя)
  3. при наведении (li:hover > ul). Делаем меню полностью НЕпрозразным (opacity: 1) и видимым (visibility:visible)
  4. Добавляем анимацию transition: opacity 1s easy-out;
  5. Наслаждаемся