Кросс-браузерное одноуровневое вертикальное меню без JavaScript
Создание вертикального кросс-браузерного меню без использования JavaScript — чистый CSS!
Так случилось, что срочно понадобилось сделать горизонтальное меню, причем для проекта, который нужно было сдавать вчера
Естественно, самому писать не было времени, нужно было взять что-то готовое. Я недавно прочитал статью «Всплывающее гибридное меню на CSS», поэтому взял код именно оттуда. Но не в этом дело. У подавляющего большинства меню камнем преткновения становится IE6 — а всё из-за того, что он понимает :hover только для тэга <a>. Когда-то у меня был заказчик, требовавший, чтобы всё одинаково работало с выключенным JavaScript'ом; вспомнив его, я решил попробовать сделать меню, работающее безо всякого JavaScript. Так получилось, что попутно я нашел еще один очень интересный глюк в IE6 (о нём далее), в борьбе с которым у меня получилось вертикальное меню.
Друг заметил, что смысла уродоваться не было, ибо есть whatever:hover, который применяется многими разработчиками. Для всех, кто такого же мнения, отмечу, что whatever:hover — это HTC-компонент, суть JavaScript, вследствие чего при выключенном JavaScript работать не будет. Помимо этого, whatever:hover не будет работать при динамическом построении меню. И еще его использование негативно сказывается на отклик IE при начальном рендеринге (в смысле, когда весь DOM уже загружен, и наступило время выполнять deferred-скрипты и грузить картинки).
Приступим.
Разметку я взял все из той же статьи, она (разметка) имеет следующий вид:
<li>
<a href="#">Renaissance</a>
<ul>
<li><a href="#">Brunelleschi</a></li>
<li><a href="#">Alberti</a></li>
<li><a href="#">Palladio</a></li>
<li><a href="#">Michelangelo</a></li>
<li><a href="#">Bramante</a></li>
</ul>
</li>
<li>
<a href="#">Art Nouveau</a>
<ul>
<li><a href="#">Mackintosh</a></li>
<li><a href="#">Guimard</a></li>
<li><a href="#">Horta</a></li>
<li><a href="#">van de Velde</a></li>
</ul>
</li>
<li>
<a href="#">Modern</a>
<ul>
<li><a href="#">Sullivan</a></li>
<li><a href="#">Le Corbusier</a></li>
<li><a href="#">Mies</a></li>
<li><a href="#">Gropius</a></li>
<li><a href="#">Yamasaki</a></li>
</ul>
</li>
<li>
<a href="#">Postmodern</a>
<ul>
<li><a href="#">Venturi</a></li>
<li><a href="#">Eisenman</a></li>
<li><a href="#">Stern</a></li>
<li><a href="#">Graves</a></li>
<li><a href="#">Gehry</a></li>
</ul>
</li>
<li>
<a href="#">Digital</a>
<ul>
<li><a href="#">Xenakis</a></li>
<li><a href="#">Lynn</a></li>
<li><a href="#">Diller+Scofidio</a></li>
<li><a href="#">Zellner</a></li>
<li><a href="#">Hadid</a></li>
</ul>
</li>
</ul>
Начнём с браузеров, поддерживающих CSS 2.1. Таблица стилей для них будет иметь следующий вид:
height: 2em;
}
#menu, #menu ul, #menu ul li {
list-style: none;
display: block;
margin: 0;
padding: 0;
}
#menu li {
float: left;
height: 2em;
line-height: 2em;
position: relative;
}
#menu li a {
display: block;
border: 1px solid red;
vertical-align: middle;
padding: 0 .5em;
}
#menu li ul {
display: none;
position: absolute;
top: 2em;
left: 0;
margin: 1px 0 0 0;
padding: 0;
background: #FFF;
width: 100px;
}
#menu li:hover ul {
display: block;
}
#menu li ul li {
float: none;
width: 100%;
}
#menu li ul li a {
border: 1px solid red;
vertical-align: middle;
padding: 0 .5em;
}
Вся магия содержится в двух правилах:
display: none;
position: absolute;
top: 2em;
left: 0;
margin: 1px 0 0 0;
padding: 0;
background: #FFF;
width: 100px;
}
#menu li:hover ul {
display: block;
}
Первое задаёт размеры/позицию выпадающего меню; позиция вычисляется относительно родительского элемента <li>. Второе же правило показывает выпадающее меню при наведении курсора мыши на заголовок. Ничего сложного.
Презентационные стили:
margin-left: -1px;
}
#menu li ul li + li {
margin-top: -1px;
}
#menu li a {
outline: 0;
text-decoration: none;
}
#menu li:hover a, #menu li a:hover a {
background: #EEE;
}
#menu li li a, #menu li:hover li a, #menu li a:hover li a {
background: #FFF;
}
#menu li ul li:hover a, #menu li ul li a:hover {
background: #EAFBFC;
}
Первые два правила позиционируют элементы списков так, чтобы border-right (border-bottom) предыдущего элемента совпадал с border-left (border-top) следующего.
Теперь переходим к IE6. В нём не работает второе магическое правило (#menu li:hover ul), так как IE6 воспринимает :hover только на элементах <a>.
Первая мысль — закрывать для IE6 <a> не до списка, а после. Мысль хорошая, но просмотр дерева в IE Developer Toolbar показал, что IE как-то «криво» понимает код (в принципе, здесь Ишак прав: невалидный код каждый волен понимать так, как может). Следовательно, надо список «обернуть» во что-нибудь, но так, чтобы <a> нормально работал. И совершенно случайно
я вспомнил один случай, когда разгребал одну ужасную табличную вёрстку (а уровень вложенности таблиц переваливал там за 16), и открытый тэг <a> превратил всю таблицу в сплошную гиперссылку. Чем чёрт не шутит, я решил попробовать.
Код стал таким:
<a href="#">Renaissance<!--[if IE 7]><!--></a><!--<![endif]-->
<!--[if lte IE 6]><table><tbody><tr><td><![endif]-->
<ul>
<li><a href="#">Brunelleschi</a></li>
<li><a href="#">Alberti</a></li>
<li><a href="#">Palladio</a></li>
<li><a href="#">Michelangelo</a></li>
<li><a href="#">Bramante</a></li>
</ul>
<!--[if lte IE 6]></td></tr></tbody></table></a><![endif]-->
</li>
Получилось! Ишак нормально распарсил код и сгенерировал нормальное дерево:
![]()
Теперь нужно немного поколдовать, чтобы заставить всё работать как надо.
Первое, что приходит в голову — это такие вот стили:
display: block;
}
* html #menu li a:hover {
position: static;
}
* html #menu li a:hover table {
display: block;
}
* html #menu table {
position: absolute;
border-collapse: collapse;
top: 0;
left: -1px;
display: none;
}
Иными словами, мы всю магию перенесли на уровень выше (так как теперь у нас <ul> обёрнут <table>). Правило position: static в * html #menu li a:hover — это вуду, без этого не работает
(как вариант, можно задать background с цветом, отличным от transparent).
И тут начинаются проблемы. Я, конечно, всегда подозревал, что IE написан очень криво, но чтобы так криво… После нескольких наведений на меню получается такая картина:
То есть таблицы как бы нет (display: none), но место в потоке она занимает, и при этом хотя текста дочерних элементов не видно, но border остался. Нет таблицы, и в то же время есть. Чудеса.
От таких чудес невольно опускаются руки. Потратив пару часов на эксперименты, я чудом нашел рабочую комбинацию: вместо display: none/block нужно использовать visibility: visible/hidden. Не знаю, почему, но оно работает. Но всё же мне интересно, что курили разработчики, писавшие Ишака?
Страница с рабочим кросс-браузерным меню находится здесь. Никакого JavaScript, только CSS!
Update: еще одна версия вертикального меню.
Вложения:
Автор: Vladimir; опубликовано в: CSS, HTML; метки: CSS, HTML, IE6, whatever:hover, кросс-браузерное решение, меню, ошибка, разметкаМар
2008
Комментарии к статье «Кросс-браузерное одноуровневое вертикальное меню без JavaScript» (32) »
Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.
Оставить комментарий к записи «Кросс-браузерное одноуровневое вертикальное меню без JavaScript» Отменить ответ
गते गते पारगते पारसंगते बोधि स्वाहा
Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.


Leo, вот исправленная версия: http://blog.sjinks.pro/test/menu/leo.html
а что делает строка,с которой все начинается и без которой ничего в эксплоере не работает?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">Говорит, что документ должен разбираться в соответствии со спецификацией XHTML 1.0 Transitional.
В теме написано «вертикально меню», но я вижу горизонтальное меню, ли я не прав?
Смотря как смотреть.
Полоса меню горизонтальная, но сам выпадающее меню вертикальное.
а ты случайно не из Севастополя ?
Далеко не случайно из Севастополя
то-то вижу лицо знакомое на аватарке. в школе видел видимо
Скорее, в офисе СевСтара. Школу я очень давно окончил.
так что точно в 7й видел
Упер у товарища Стью: cssplay.co.uk
Что именно? Меню я брал отсюда, ссылка тоже вроде есть. Что не так?
Спасибо большое за менюшку, особенно за момент с упаковыванием списка в ссылку посредством таблицы для осла. Правда есть еще пару багов.
Во-первых: на малых разрешениях и/или большом размере меню, оно переноситься на вторую строчку и ссылки из него начинают налазить на выпадающее подменю. Для большинства браузеров достаточно установить z-index списку подменю:
z-index: 1; /* или какой-нить другой */
}
Так как IE трактует z-index по-своему, к нему другой подход – нужно установить z-index каждому li-элементу основного меню по правилу – первому самый высокий, второму ниже, третий еще ниже и тд. Это объясняется тем, что IE сравнивает z-index родителей, игнорируя детей. Если им явно не задать z-index в ниспадающем порядке, он считает z-index каждого следующего элемента выше, чем предыдущего. Это мой вариант решения, можно и по-другому сообразить.
Во-вторых: В IE6 список подменю мерцает, когда по нему ведешь курсором. Есть ли от этого лекарство(желательно без js)?
Тарас, а используется сторонняя JavaScript-библиотека типа Scriptaculous?
У меня что-то подобное было, лечилось исправлением библиотеки.
С этим уже разобрался. Действительно была проблема с js – событие mousemove висело на document, что видимо перегружало несчастный IE. -)
Свою менюшку довел более-менее до ума, но по дороге наткнулся еще на одну проблему – непонятные отступы в IE. В 5-7 был отступ от последнего пункта подменю к нижней границе подменю, в 5, к тому же, еще и к левой границе (у Вас он тоже есть). В ходе экспериментов пришел к тому, что в IE display:block для li-элементов подменю не критичен и именно из-за него появляется отступ слева в 5-м. Но его отсутствие не дает, в опере например, задать фон ссылке, вложенной в него. Потому убрал его хаком:
{
-display: auto;
}
Убрать нижний отступ удалось заданием height и line-height для ссылки, вложенной в li-элемент подменю (у Вас эти свойства задаются самому li, что в моем случае не помогло):
{
height: 1.4em;
line-height: 1.4em;
}
И еще, возможно поможете с моей локальной проблемой? Мне нужно, чтобы подменю тянулось по содержимому. Пока что задал следующее:
{
width: 100%;
white-space: nowrap;
}
Как всегда IE выделился – он игнорирует width:100%, если родительскому элементу строго не задать размер. Как это обойти?
Вот у меня это тоже не получается
Владимир спасибо! Метод без скриптов очень интересен. Но есть одна проблема: через
<!--[ if IE 7 ]><!--></a><!--меню не работает в IЕ8, а там дальше если появится IЕ9, то ситуация будет аналогичная. Я не профессионал, поэтому сам найти ответ не могу. Если возможно подскажите решения. Спасибо!Сергей, попробуйте
<!--[if gte IE 7]><!--></a><!--<![endif]-->вместо
<!--[if IE 7]><!--></a><!--<![endif]-->По возможности отпишитесь, пожалуйста, о результатах — у меня пока нет возможности проверить. Спасибо.
Объясните чайнику, поисковики будут видеть и индексировать страницы в таком меню?
Да.
Прошу прощения за спам…не обратил внимания на разрешенные тэги,
Доброго времени суток Vladimir!
Объясните пожалуйста смысл условного комментария:
<!--[if lte IE 6]><![endif]-->
я так понимаю если ие7 закрывай тэг а,если ие6 тогда конструкция с таблицей,а это что???
<!–я имею ввиду до и после закрывающего тэга?
Этот код:
<a href="#">Renaissance<!--[if IE 7]><!--></a><!--<![endif]-->
<!--[if lte IE 6]><table><tbody><tr><td><![endif]-->
<ul>
<li><a href="#">Brunelleschi</a></li>
</ul>
<!--[if lte IE 6]></td></tr></tbody></table></a><![endif]-->
</li>
нормальные браузеры и IE7 увидят так:
<a href="#">Renaissance</a>
<ul>
<li><a href="#">Brunelleschi</a></li>
</ul>
</li>
В случае с IE6 будет так:
<a href="#">Renaissance
<table><tbody><tr><td>
<ul>
<li><a href="#">Brunelleschi</a></li>
<li><a href="#">Alberti</a></li>
<li><a href="#">Palladio</a></li>
<li><a href="#">Michelangelo</a></li>
<li><a href="#">Bramante</a></li>
</ul>
</td></tr></tbody></table></a>
</li>
Получится грязный хак: IE6 увидит таблицу внутри тэга
<a>. Он опирается на один из багов IE6: тэгtableне закроет тэгa(как сделал бы другой нормальный браузер), в результате чего вся таблица будет помещена внутрь. Благодаря этому список (ul) у нас также окажется внутри тэгаa, и правила с:hoverбудут работать.Если же делать без таблицы:
<a href="#">Renaissance
<ul>
<li><a href="#">Brunelleschi</a></li>
<li><a href="#">Alberti</a></li>
<li><a href="#">Palladio</a></li>
<li><a href="#">Michelangelo</a></li>
<li><a href="#">Bramante</a></li>
</ul>
</a>
</li>
То IE6 поместит закрывающий
</a>передul, в результате чего правила с:hoverработать не будут.