Выпадающее меню на CSS

перейти к комментариям


Выпадающее меню на CSS

Про выпадающие меню на CSS написано много статей, взять хотя бы Suckerfish Dropdowns, чью методику я хочу немного развить.

Данная статья предназначена для новичков в CSS, желающих научиться делать красивые меню без использования JavaScript, сохраняя при этом чистый код HTML-страниц. Я постараюсь объяснить каждое применяемое правило, показывая промежуточные результаты.

Вот к чему мы придем: финальное меню.

Разметка

Начнем мы с семантической разметки. Наше меню, содержащее сразу три уровня — это простой ненумерованный список, а каждое подменю в нем — это вложенный список. Такой подход имеет ряд преимуществ:

В HTML это выглядит примерно так:

<ul id="nav">
  <li><a href="#null">Домой</a></li>
  <li><a href="#null">Каталог</a>
    <ul>
      <li><a href="#null">Вся продукция</a>
        <ul>
          <li><a href="#null">По дате</a></li>
          <li><a href="#null">Производители</a></li>
          <li><a href="#null">Другое</a></li>
        </ul>
      </li>
    </ul>
  </li>
  ...
</ul>

Пусть вас не пугает вложенность списков. Главное — следить за правильностью открытия/закрытия тегов. В частности, каждый вложенный тег <ul> должен содержаться внутри тега <li>.

Вот что мы пока имеем: шаг 1.

Немного стиля

Теперь мы добавим несколько кусочков CSS в наш код:

#nav, #nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid #000;
  background: #515151;
  float: left;
  width: 100%;
}
#nav li {
  float: left;
  position: relative;
  background: #515151;
  back\ground: none;
}
#nav li ul {
  display: none;
}

Этими тремя правилами мы сделали следующие вещи:

Когда блочные элементы имеют свойство float, они становятся «плавающими». Это позволяет выстраивать их в одну линию друг за другом: ? ? ? ? ?. Подробнее про свойство float.

Так как все элементы списка <ul id="nav"> теперь «плавающие», то сам список «схлопывается». Это происходит из-за невозможности вычислить реальную высоту элемента, который содержит другие «плавающие» элементы.

Для борьбы с этой напастью существует несколько методов, однако они могут не работать в новом IE 7. Поэтому здесь я решил использовать метод FNE, заключающийся в присвоении свойства float:left самому контейнеру. Это избавляет нас от «схлопывания» списка, но заставляет нижележащие элементы обтекать меню справа. Именно поэтому мы указываем ширину для всего меню 100% — чтобы справа просто не оставалось места.

Кроме того, мы могли указать свойство clear:both для элемента, следующего сразу за меню. Это заставило бы его опуститься ниже всех «плавающих» элементов.

Что мы получили: шаг 2.

Добавим немного оформления нашим ссылкам:

#nav a {
  color: #fff;
  text-decoration: none;
  display: block;
  width: 120px;
  padding: 4px 10px;
  background: url(dot.png) repeat-y right;
}
#nav a:hover {
  color: #000;
  background: #ccc;
}
#nav li:hover {
  background: #333;
}

Первым правилом мы оформили ссылки (чтобы они больше походили на кнопки):

Задание ширины является необходимостью при использовании свойства float для всех элементов кроме элемента <img>.

Селектор #nav a:hover срабатывает в том случае, когда мы подводим курсор к ссылке, а #nav li:hover — когда подводим его к элементу списка. Второй случай понадобиться нам для того, чтобы в меню оставался «след» наших перемещений (мы это увидим далее).

Промежуточный результат: шаг 3.

Мы же не зря назвали это меню «выпадающим»

Мы дописываем к уже существующему правилу:

#nav li ul {
  display: none;
}

следующие инструкции:

#nav li ul {
  display: none;
  position: absolute;
  background: url(fone-tr.png);
  padding: 8px 0;
  width: 138px;
}

Выражение position:absolute служит для абсолютного позиционирования подменю относительно элемента <li> верхнего уровня.

Когда мы имеем родительский элемент, позиционированный «относительно» (т. е. имеющий position:relative), все позиционированные абсолютно элементы, содержащиеся в нем, будут позиционироваться относительно родительского элемента, а не относительно всей страницы.

#nav li li a {
  width: 118px;
  background: none;
}
#nav li:hover ul {
  display: block;
}

Вся магия выпадающего меню заключена в строке display:block для #nav li:hover ul. Именно она заставляет подменю «появиться» при подводе курсора к ссылке, сменяя ранее установленный режим display:none:

#nav li:hover li ul {
  display: none;
  width: 138px;
  top: -9px;
  left: 133px;
}
#nav li:hover li:hover ul {
  display: block;
}

Ширина нашего подменю равна 138 пикселям из-за того, что мы вычитаем 2 пикселя от границ с каждой стороны: 140 – 1 – 1 = 138 пикселей.

Селектор #nav li:hover li ul оказывает влияние на подменю 3-го уровня. Мы его сдвигаем влево на ширину 133 пикселя (величина чисто эмпирическая), а также немного вверх (чтобы оно оказалось на одном уровне с активной ссылкой). Теперь, при наведении мышки, наше подменю будет выскакивать справа от ссылки.

Выпадающее меню (пока не для IE): шаг 4.

Фактор IE

Жизнь многих веб-разработчиков стала бы проще если бы не было Internet Explorer. Ситуация начинает улучшаться в связи с выходом седьмой версии, но до повсеместного ее распространения еще очень далеко.

В ранних версиях IE псевдокласс hover поддерживается только для элемента <a>. В нашем же случае это требуется для элемента списка <li>. Поэтому мы будем использовать простую функцию JavaScript для нужной нам реакции на подведение мышки:

<script type="text/javascript">
  jsHover = function() {
    var hEls = document.getElementById("nav").getElementsByTagName("LI");
    for (var i=0, len=hEls.length; i<len; i++) {
      hEls[i].onmouseover=function() { this.className+=" jshover"; }
      hEls[i].onmouseout=function() { this.className=this.className.replace(" jshover", ""); }
    }
  }
  if (window.attachEvent && navigator.userAgent.indexOf("Opera")==-1) window.attachEvent("onload", jsHover);
</script>

Это позволяет «прицепить» класс jshover к любому элементу <li>, над которым проходит курсор. Эта функция работает только в Internet Explorer — для других браузеров она просто не нужна.

Теперь, чтобы меню заработало в IE, добавим к четырем уже существующим правилам по дополнительному селектору li.jshover:

#nav li:hover,
#nav li.jshover {
  ...
}
#nav li:hover ul,
#nav li.jshover ul {
  ...
}
#nav li:hover li ul,
#nav li.jshover li ul {
  ...
}
#nav li:hover li:hover ul,
#nav li.jshover li.jshover ul {
  ...
}

Теперь можете смотреть и в IE: шаг 5, финальный.

Дополнительная информация

В качестве фона для подменю используется полупрозрачный PNG-файл. IE 6 не поддерживает полупрозрачность, но вы можете это исправить.

Из-за использования полупрозрачной картинки мне пришлось убрать цвет фона. Это привело к тому, что при отключенных картинках буквы подменю становятся не видны. Выход один: задать цвет фона для #nav li, потеряв при этом полупрозрачность.

Я использую хак back\ground:none;, чтобы принудительно задать цвет меню для IE 5. Если этого не сделать, то в этом браузере фон не отображается. Наверное это можно исправить как-то по-другому, но у меня нет желания разбираться со всеми его причудами.

UPD. akella подсказал, что при некоторых настройках системы меню может распираться в Опере. Это происходит из-за использования для всех размеров «абсолютных» единиц px. Пиксели — это зло.
Поэтому я сделал второй вариант полностью на em (кроме ширины границы). И именно из-за этой однопиксельной рамки могут появляться небольшие зазоры при увеличении размера шрифта. Выход — не используйте границу =)

Ссылки по теме


Комментарии RSS для комментариев

  1. # Никита 4521 дн. назад:

    Хороший материал. Симпатичное меню получается.

    Вот только этот осёл (IE) всюду лезет. В конкретном случае заставляет использовать JavaScript.

    Возможно будет полезно:
    PNG, альфа прозрачность и Internet Explorer
    Свойство :hover для любого CSS элемента

  2. # Spr!tE 4521 дн. назад:

    Никита: спасибо. а вот тут как раз обсуждается что лучше: .htc или JS (внизу страницы).
    Я лично тоже предпочитаю JS

  3. # артем — 4446 дн. назад:

    Спасибо вам большое за скрипт очень помогли :) !!!

  4. # Polina — 4410 дн. назад:

    Спасибо. Информация на этой странице мне очень пригодилась.

  5. # Артемий 4368 дн. назад:

    Полезный материал. Вот тока под Mac IE работать не хочет ни в какую...

  6. # Vlad 4354 дн. назад:

    Спасибо за блог, день потратил на поиски не глючного дроп-меню.

  7. # Алена — 4331 дн. назад:

    не понимаю, что-то все делаю пошагово, но получается совсем не то, что у вас :(

  8. # Александр Шабуневич — 4326 дн. назад:

    не понимаю, что-то все делаю пошагово, но получается совсем не то, что у вас :(

    Если есть конкретные вопросы, то можно писать на почту. Если будет время — могу помочь

  9. # neznaika 4323 дн. назад:

    Отличное пособие, все пригодилось. Считаю, что прозрачностью можно пренебречь. Проверенно на индексацию: отлично.

  10. # DA — 4317 дн. назад:

    Спасибо за информацию! Очень пригодилась! Только у меня есть один вопросик... Можно ли сделать css-меню (работающее в IE5/6) выпадающее не на наведение мышью, а на клик, и при этом оно и сворачиваться должно так же по клику??? То есть написать в css можно все, а где бы найти скрипт для java… :(

  11. # boa — 4312 дн. назад:

    Respect! Однозначно!
    Самое главное – «Вы разделяете содержимое и представление». С точки зрения программизма – пять баллов по пятибальной.

  12. # Михаил — 4304 дн. назад:

    Все хорошо, но как отцентрировать меню?

  13. # Suworow — 4294 дн. назад:

    Задали ширину каждой ссылки 120 пикселей (справедливости ради, надо сказать, что реальная ширина равна 140 (120 + 10 + 10) пикселям, так как в нее включаются и отступы)

    Если не ошибаюсь, то padding не изменит реалную ширину блока. Он задаст отступ котнтента внутри блока. А ширина в себя это тоже будет включать. Вот если было бы
    margin: 5px 10px;
    тогда да. Хотя могу ошибаться...

  14. # Александр Шабуневич — 4294 дн. назад:

    Если не ошибаюсь, то padding не изменит реалную ширину блока.

    Ошибаетесь. Реальная ширина в себя включает и padding и margin и даже border. Не включает только IE 6 в режиме обратной совместимости, но он это делает вопреки спецификации.

  15. # Анатолий — 4292 дн. назад:

    Супер! Как я мучился с выпадающим меню.. А тут бац! И все супер...

    Только один вопрос..
    Имеем:
    Ширина всего меню 100%, в меню 5 пунктов.
    Я хочу, чтобы каждый из них занимал по 20%.
    Что бы хорошо выглядело на разных экранах..
    Но никак не получается это сделать!!
    Помогите пожалуйста!

  16. # Александр Шабуневич — 4292 дн. назад:

    Ширина всего меню 100%, в меню 5 пунктов.
    Я хочу, чтобы каждый из них занимал по 20%.
    Что бы хорошо выглядело на разных экранах..

    Это тяжело, так как в эксплорере иногда 20×5 != 100%, а равно 100% + 1 пиксель. Поэтому будет слетать вниз при изменении размера окна. Можно конечно повозиться с разными вариантами — все величины надо указывать в процентах. Но не уверен, что будет идеально работать.

  17. # В мозилле не пашет — 4272 дн. назад:

    в мозилле не пашет

  18. # Z1go — 4270 дн. назад:

    Выношу респект автору (:
    Т.к. сам являюсь линухоидом, то посмотрел менюшку тока в Firefox 2 и в Konqueror (KHTML). Работает «на ура» даже в «Завоевателе», что удивляет (: Следовательно, работаеть будет даже в «Сафари», что ещё более удивительно ((:

  19. # Олег 4198 дн. назад:

    Руководство по созданию выпадающего меню отличное.
    Единственно хочу предупредить, код стилей для меню желательно прописывать на каждой странице сайта, а не в отдельном файле (style.css). Иначе, при загрузки страниц с подобным меню, если код стилей для меню прописан в отдельном файле (style.css), в течении первых 2-3 секунд возникает эффект размытого меню (в виде длинного списка), так же не красиво смотрятся если их решат сохранить на компьютере с помощью Oper-ы. Подобного не происходит, если код стилей прописан на каждой странице.

  20. # divlesika — 4186 дн. назад:

    А можно ли с помощью CSS сделать эффект плавного (не такого быстрого по скорости) раскрытия меню? (Как где-то читала «для посетителей с замедленной моторикой для лучшего их восприятия» :-).

  21. # Андрей 4185 дн. назад:

    А вот мне помоч может кто, как мне ее вертикально сделать? =) И Вот вообще супер бы было если задержку сделать хотяб сикунду....

  22. # Антон — 4174 дн. назад:

    Интересно, а можно сделать меню для пунктов нефиксированной ширины?

  23. # .n1tro — 4172 дн. назад:

    Спасибо, очень помогло.

  24. # Евгений 4125 дн. назад:

    Просто отлично!! На таких как Вы, держится Российский интернет! Так держать!

  25. # Malefic — 4123 дн. назад:

    Абзац...
    Парился с джавой, а тут такая прелесть под ногами валяется!
    Спасибо!

  26. # Илья — 4120 дн. назад:

    Спасибо, действительно позитивнополезная статья :)

  27. # Федька — 4092 дн. назад:

    Лично меня раздражает когда такие задачи с меню выводят внагрузку на CSS (как полупрозрачное меню сайта imb.ru). Значительно притормаживает все это в IE.

    А JavaScript в работе при решении подобных задач так не издеваеся над юзером.

    А если верить статистике его юзают около 78 процентов. По крайней мере его юзают все неискушенные.

  28. # Сергей 4081 дн. назад:

    Полезная и актуальная статья даже сейчас.
    Помогает дизайнеру-вебмастеру.

  29. # Константин — 4074 дн. назад:

    Пытаюсь сделать, что бы ширина меню 3 уровня зависела от количества элементов, но как-то не вытанцовывается

Что вы об этом думаете?


Стиль админки Textpattern Пример перепланировки