Выпадающее меню на CSS
Опубликовал Александр Шабуневич — 30 Март 2006, 23:32
Про выпадающие меню на CSS написано много статей, взять хотя бы Suckerfish Dropdowns, чью методику я хочу немного развить.
Данная статья предназначена для новичков в CSS, желающих научиться делать красивые меню без использования JavaScript, сохраняя при этом чистый код HTML-страниц. Я постараюсь объяснить каждое применяемое правило, показывая промежуточные результаты.
Вот к чему мы придем: финальное меню.
Разметка
Начнем мы с семантической разметки. Наше меню, содержащее сразу три уровня — это простой ненумерованный список, а каждое подменю в нем — это вложенный список. Такой подход имеет ряд преимуществ:
- Код вашего меню занимает мало места
- Меню становится доступным для поисковиков и альтернативных клиентов
- Вы разделяете содержимое и представление, контролируя оформление только при помощи CSS
В 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;
}
Этими тремя правилами мы сделали следующие вещи:
- Убрали буллеты из нашего списка —
list-style:none
- Обнулили отступы
padding
иmargin
у элементов меню - Украсили меню границей и задним фоном. Свойство
back\ground:none
служит для задания прозрачного фона во всех браузерах кроме IE 5. Я поясню это позднее - Заставили каждый элемент списка
<li>
, встать на одну линию при помощи правилаfloat:left
- Скрыли подменю 2-го и 3-го уровня, указав
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;
}
Первым правилом мы оформили ссылки (чтобы они больше походили на кнопки):
- Каждому элементу
<a>
мы присвоили свойствоdisplay:block
, что дало нашим ссылкам ширину и высоту - Убрали подчеркивание при помощи
text-decoration:none
- Задали ширину каждой ссылки 120 пикселей (справедливости ради, надо сказать, что реальная ширина равна 140 (120 + 10 + 10) пикселям, так как в нее включаются и отступы)
Задание ширины является необходимостью при использовании свойства 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
(кроме ширины границы). И именно из-за этой однопиксельной рамки могут появляться небольшие зазоры при увеличении размера шрифта. Выход — не используйте границу =)
Комментарии
Хороший материал. Симпатичное меню получается.
Вот только этот осёл (IE) всюду лезет. В конкретном случае заставляет использовать JavaScript.
Возможно будет полезно:
PNG, альфа прозрачность и Internet Explorer
Свойство :hover для любого CSS элемента
Никита: спасибо. а вот тут как раз обсуждается что лучше: .htc или JS (внизу страницы).
Я лично тоже предпочитаю JS
Спасибо вам большое за скрипт очень помогли :) !!!
Спасибо. Информация на этой странице мне очень пригодилась.
Полезный материал. Вот тока под Mac IE работать не хочет ни в какую…
Спасибо за блог, день потратил на поиски не глючного дроп-меню.
не понимаю, что-то все делаю пошагово, но получается совсем не то, что у вас :(
Если есть конкретные вопросы, то можно писать на почту. Если будет время — могу помочь
Отличное пособие, все пригодилось. Считаю, что прозрачностью можно пренебречь. Проверенно на индексацию: отлично.
Спасибо за информацию! Очень пригодилась! Только у меня есть один вопросик… Можно ли сделать css-меню (работающее в IE5/6) выпадающее не на наведение мышью, а на клик, и при этом оно и сворачиваться должно так же по клику??? То есть написать в css можно все, а где бы найти скрипт для java… :(
Respect! Однозначно!
Самое главное – «Вы разделяете содержимое и представление». С точки зрения программизма – пять баллов по пятибальной.
Все хорошо, но как отцентрировать меню?
Задали ширину каждой ссылки 120 пикселей (справедливости ради, надо сказать, что реальная ширина равна 140 (120 + 10 + 10) пикселям, так как в нее включаются и отступы)
Если не ошибаюсь, то padding не изменит реалную ширину блока. Он задаст отступ котнтента внутри блока. А ширина в себя это тоже будет включать. Вот если было бы
margin: 5px 10px;
тогда да. Хотя могу ошибаться…
Ошибаетесь. Реальная ширина в себя включает и padding и margin и даже border. Не включает только IE 6 в режиме обратной совместимости, но он это делает вопреки спецификации.
Супер! Как я мучился с выпадающим меню.. А тут бац! И все супер…
Только один вопрос..
Имеем:
Ширина всего меню 100%, в меню 5 пунктов.
Я хочу, чтобы каждый из них занимал по 20%.
Что бы хорошо выглядело на разных экранах..
Но никак не получается это сделать!!
Помогите пожалуйста!
Это тяжело, так как в эксплорере иногда 20×5 != 100%, а равно 100% + 1 пиксель. Поэтому будет слетать вниз при изменении размера окна. Можно конечно повозиться с разными вариантами — все величины надо указывать в процентах. Но не уверен, что будет идеально работать.
в мозилле не пашет
Выношу респект автору (:
Т.к. сам являюсь линухоидом, то посмотрел менюшку тока в Firefox 2 и в Konqueror (KHTML). Работает «на ура» даже в «Завоевателе», что удивляет (: Следовательно, работаеть будет даже в «Сафари», что ещё более удивительно ((:
Предлагаю немного переделанный вариант и упрощенный до минимума + не нужны абсолютно никакие скрипты.
Для правильной работы меню нужен либо MSIE не ниже 7-й версии, либо любая Opera, либо любой Mozilla Firefox.
На остальных браузерах не проверялось :(
#nav, #nav ul {
margin: 0;
padding: 0;
list-style: none;
float: left;
width: 100%;}
#nav li {
display: block;
float: left;
position: relative;
width: 150pt;}
#nav a {
display: block;
background-color: cornflowerblue;
padding: 10pt;
color: white;
text-decoration: none;}
#nav a:hover {
background-color: red;
text-decoration: underline;}
#nav li ul {
display: none;
position: absolute;
width: 150pt;}
#nav li:hover ul {
display: block;
width: 150pt;}
#nav li:hover li ul {
display: none;
left: 150pt;
top: 0pt;}
#nav li:hover li:hover ul {
display: block;
width: 150pt;}
Большое спасибо автору за статью – очень помогла.
Блин! Классно, все четко и подробно. Спасибо автору.
Статья полезная, но как сделать чтобы меню было по центру страницы?
Класс. Это именно то что нужно. Спасибо. Единственно не совсем пойму, у меня все работает и в IE 7 без применения яваскрипта. Очень хочется так и оставить.
Руководство по созданию выпадающего меню отличное.
Единственно хочу предупредить, код стилей для меню желательно прописывать на каждой странице сайта, а не в отдельном файле (style.css). Иначе, при загрузки страниц с подобным меню, если код стилей для меню прописан в отдельном файле (style.css), в течении первых 2–3 секунд возникает эффект размытого меню (в виде длинного списка), так же не красиво смотрятся если их решат сохранить на компьютере с помощью Oper-ы. Подобного не происходит, если код стилей прописан на каждой странице.
А можно ли с помощью CSS сделать эффект плавного (не такого быстрого по скорости) раскрытия меню? (Как где-то читала «для посетителей с замедленной моторикой для лучшего их восприятия» :-).
А вот мне помоч может кто, как мне ее вертикально сделать? =) И Вот вообще супер бы было если задержку сделать хотяб сикунду…
Интересно, а можно сделать меню для пунктов нефиксированной ширины?
Спасибо, очень помогло.
#nav, #nav ul {
margin: 0;
padding: 0;
list-style: none;
float: left;
width: 19%;
}