Ars Longa, Vita Brevis

Создание вертикального кросс-браузерного меню без использования JavaScript — чистый CSS!

Так случилось, что срочно понадобилось сделать горизонтальное меню, причем для проекта, который нужно было сдавать вчера :-) Естественно, самому писать не было времени, нужно было взять что-то готовое. Я недавно прочитал статью "Всплывающее гибридное меню на CSS", поэтому взял код именно оттуда. Но не в этом дело. У подавляющего большинства меню камнем преткновения становится IE6 — а всё из-за того, что он понимает :hover только для тэга <a>. Когда-то у меня был заказчик, требовавший, чтобы всё одинаково работало с выключенным JavaScript'ом; вспомнив его, я решил попробовать сделать меню, работающее безо всякого JavaScript. Так получилось, что попутно я нашел еще один очень интресный глюк в IE6 (о нём далее), в борьбе с которым у меня получилось вертикальное меню.

Друг заметил, что смысла уродоваться не было, ибо есть whatever:hover, который применяется многими разработчиками. Для всех, кто такого же мнения, отмечу, что whatever:hover — это HTC-компонент, суть JavaScript, вследствие чего при выключенном JavaScript работать не будет. Помимо этого, whatever:hover не будет работать при динамическом построении меню. И еще его использование негативно сказывается на отклик IE при начальном рендеринге (в смысле, когда весь DOM уже загружен и наступило время выполнять deferred-скрипты и грузить картинки).

Приступим.
Разметку я взял все из той же статьи, она (разметка) имеет следующий вид:

[-]
View Code HTML
<ul id="menu">
    <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. Таблица стилей для них будет иметь следующий вид:

[-]
View Code CSS
#menu {
    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;
}

Вся магия содержится в двух правилах:

[-]
View Code CSS
#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;
}

Первое задаёт размеры/позицию выпадающего меню; позиция вычисляется относительно родительского элемента <li>. Второе же правило показывает выпадающее меню при наведении курсора мыши на заголовок. Ничего сложного.

Презентационные стили:

[-]
View Code CSS
#menu > li + 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> превратил всю таблицу в сплошную гиперссылку. Чем чёрт не шутит, я решил попробовать.

Код стал таким:

[-]
View Code HTML
<li>
    <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>

Получилось! Ишак нормально распарсил код и сгенерировал нормальное дерево:

XML-дерево, сгенерированное IE

Теперь нужно немного поколдовать, чтобы заставить всё работать как надо.
Первое, что приходит в голову — это такие вот стили:

[-]
View Code CSS
* html #menu li ul {
    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 написан очень криво, но чтобы так криво… После нескольких наведений на меню получается такая картина:

Ошибка IE6

То есть таблицы как бы нет (display: none), но место в потоке она занимает, и при этом хотя текста дочерних элементов не видно, но border остался. Нет таблицы, и в то же время есть. Чудеса.

От таких чудес невольно опускаются руки. Потратив пару часов на эксперименты, я чудом нашел рабочую комбинацию: вместо display: none/block нужно использовать visibility: visible/hidden. Не знаю, почему, но оно работает. Но всё же мне интересно, что курили разработчики, писавшие Ишака?

Страница с рабочим кросс-браузерным меню находится здесь. Никакого JavaScript, только CSS!

Update: еще одна версия вертикального меню.

Комментарии к статье "Кросс-браузерное одноуровневое вертикальное меню без JavaScript" (11) »

  1. [Март 23, 2008 6:34 дп] Кросс-браузерное всплывающее гибридное меню на CSS без использования JavaScript | Ars Longa, Vita Brevis:

    [...] Ars Longa, Vita Brevis Март 23, 2008 « Кросс-браузерное одноуровневое вертикальное меню без … [...]

    #1
  2. [Апрель 3, 2008 9:50 дп] Google Adsense и XHTML | Ars Longa, Vita Brevis:

    [...] тэг <object> (нечто подобное было сделано в статье “Кросс-браузерное одноуровневое вертикальное меню без JavaScript”) <!–[if gt IE 8]><!–> <object [...]

    #2
  3. [Апрель 14, 2008 9:19 пп] Аккордеон на CSS без использования JavaScript | Ars Longa, Vita Brevis:

    [...] браузерам (об этом можно прочитать в статье “Кросс-браузерное одноуровневое вертикальное меню без …”), поэтому я сразу привожу разметку, которая подойдёт [...]

    #3
  4. [Май 13, 2008 1:50 пп] bersy:

    спасибо, в закладки

    #4
  5. [Май 13, 2008 1:55 пп] bersy:

    хмм, или кнопка закладок работает не так как я ожидал (никогда ее не использовал), или она просто не работает
    ни урл, ни тайтл, ни описание не передаются по кнопке

    #5
  6. [Май 13, 2008 4:56 пп] Vladimir:

    bersy, честно говоря, я тоже никогда этой кнопкой не пользовался и, как следствие, не обращал внимание.

    Я поправил код плагина, теперь вроде как работает (хотя странно, я использовал тот же код, что и все остальные). Приношу извинения за неудобства.

    #6
  7. [Май 13, 2008 5:21 пп] bersy:

    да, теперь порядок
    раньше пользовался пару раз только addthis.com, там все было ОК, правдо наверное это для рунета не очень удобно
    еще мои куки не запомнились, нужно заново вводить ник и мэил, мне в общем не трудно, но не все такие терпеливые :)

    #7
  8. [Май 13, 2008 5:44 пп] Vladimir:

    Bersy, а Вы зарегистрируйтесь :-) Тогда система вас запомнит.
    В любом случае, я поищу плагины, позволяющие запоминать информацию, введённую пользователем.

    #8
  9. [Май 29, 2008 5:04 пп] Leo:

    Здравствуйте.
    Я решил передрать Ваше меню и исследовать его попутно, но у меня ничего не выходит(( Особенно в IE6
    Вот код который я наодил/передрал из вашей статьи:

    [+]
    View Code CSS
    [+]
    View Code HTML

    Прошу прощения за беспокоство. Буду очень рад если скажете что я сделал не так.

    #9
  10. [Май 29, 2008 6:42 пп] Vladimir:

    Leo, всё очень просто: во-первых, Вы потеряли список, который передаёт структуру меню :-) Во-вторых, с комментариями что-то явно не так (при включённой подсветке синтаксиса это ясно видно).

    Сейчас я поправлю Ваш код и выложу его.

    #10
  11. [Май 29, 2008 6:45 пп] Vladimir:

    Leo, вот исправленная версия: http://static2.sjinks.org.ua:8080/test/menu/leo.html

    #11

RSS лента комментариев к этой записи. TrackBack URL

Оставить комментарий к записи "Кросс-браузерное одноуровневое вертикальное меню без JavaScript"

Изображения должны быть включены!

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, Вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя