1 декабря 2010 г.

Ретроспектива: СSS меню в IE6 без использования JavaScript

Как обычно - случилось, пришлось вспоминать использование различных шаманских бубнов для пожилого и по старчески вредного браузера Internet Explorer 6. И как обычно - вспомнилось, в отдаленных участках моего сознания все же были аккуратно сложены воспоминания о былых противостояниях молодых сознанием и уже не молодого браузера...

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


Решения

Для начала возьмем совершенно обыкновенную разметку ниспадающего меню, на большинстве современных сайтов можно найти идентичные варианты:
<ul class="menu">
    <li>
        <a href="#" title="Main">Main</a>
        <ul>
            <li><a href="#" title="Services">Services</a></li>
            <li><a href="#" title="Products">Products</a></li>
            <li><a href="#" title="Job">Job</a></li>
        </ul>
    </li>
    <li>
        <a href="#" title="Portfolio">Portfolio</a>
        <ul>
            <li><a href="#" title="Projects">Projects</a></li>
            <li><a href="#" title="Styles">Styles</a></li>
            <li><a href="#" title="Private clients">Private clients</a></li>
        </ul>
    </li>
    <li>
        <a href="#" title="FAQ">FAQ</a>
        <ul>
            <li><a href="#" title="Q&A">Q&amp;A</a></li>
            <li><a href="#" title="Wiki">Wiki</a></li>
        </ul>
    </li>
</ul>
И сотворим примерное работающее меню с помощью CSS:
{
    color:#555;
}
a:hover {
    color:#000;
}
.menu {
    list-style:none;
    margin:0;
    padding:0;
}
.menu li {
    position:relative;
    float:left;
    margin:0;
    padding:0.2em 1em;
}
.menu li:hover {
    background:#CCC;
}
.menu ul {
    display:none;
    position:absolute;
    left:0;
    top:1.5em;
    list-style:none;
    margin:0;
    padding:0;
    background:#EEE;
}
.menu ul li{
    float:none;
}
.menu li:hover ul{
    display:block;
}
Решение стандартное и опять таки в тех или иных вариациях используется часто, но оно не работает в IE6... А справляются с этим следующим образом:

Первое решение:

Идея состоит в том, что для всех современных брайзеров будут обрабатываться hover события для li элементов, что является естественным решением, а для IE6 будет привязываться специальный обработчик, который будет менять классы у li элементов. Таким образом в CSS придется одинаково описывать стиль представления для события наведение (:hover) и для класса с аналогичным названием (.hover).

В этом случае верстка не изменяется, но расширяется СSS:
.menu li:hover,
.menu li.hover{
    background:#CCC;
}
.menu li:hover ul,
.menu li.hover ul,{
    display:block;
}

И добавляется js-обработчик. А следовательно, и само решение становиться зависимым от JavaScript:
menuEventer = function() {
      if (document.all && document.getElementsByTagName) {
            var elements = document.getElementsByTagName("LI");
            for (var i=0; i < elements.length; i++) {
                elements[i].onmouseover=function() {
                      this.className+=" hover";
                }
                elements[i].onmouseout=function() {
                      this.className=this.className.replace(" hover", "");
                }
            }
      }
}
window.onload = menuEventer;

Такой не сложный обработчик заставляет работать меню, но такое решение, найденное в свое время Патриком Гриффитс и Даном Уэбб, нельзя назвать красивым...

Второе решение:

Второе решение частично вытекает из первого и связно с тем, что возможно избавиться от ручного расширения css и добавления js-обработчиков, но по сути оно является аналогичным. Основывается оно на использование скриптов поведения подключаемых через css-свойство behavior. Найти подобный скрипт не сложно, а разработчику достаточно только включить новое правило в css:
body {
   behavior: url("csshover3.htc");
}

Такое решение так же заставляет IE6 работать и является куда более элегантным, однако зависимость от JavaScript по-прежнему осталась...

Третье решение:

Третье решение основано на специфическом изменении верстки, которое позволило бы использовать hover для элемента a. При этом ясно, что зависимость от JavaScript уйдет и может получиться интересное решение.

Идея верстки состоит в том, чтобы вложить в элемент a все подменю (элемент ul) с его ссылками:
<ul class="menu">
    <li>
        <a href="#" title="Main">Main
            <ul>
                <li><a href="#" title="Services">Services</a></li>
                <li><a href="#" title="Products">Products</a></li>
                <li><a href="#" title="Job">Job</a></li>
            </ul>
        </a>
    </li>
    <li>
        <a href="#" title="Portfolio">Portfolio
            <ul>
                <li><a href="#" title="Projects">Projects</a></li>
                <li><a href="#" title="Styles">Styles</a></li>
                <li><a href="#" title="Private clients">Private clients</a></li>
            </ul>
        </a>
    </li>
    <li>
        <a href="#" title="FAQ">FAQ
            <ul>
                <li><a href="#" title="Q&A">Q&amp;A</a></li>
                <li><a href="#" title="Wiki">Wiki</a></li>
            </ul>
        </a>
    </li>
</ul>
В таком случае изменив CSS следующим образом можно было бы ожидать нормальное решение:
{
    color:#555;
}
a:hover {
    color:#000;
}
.menu {
    list-style:none;
    margin:0;
    padding:0;
}
.menu li {
    float:left;
    margin:0;
}
.menu a {
    display:block;
    position:relative;
    padding:0.2em 1em;
}
.menu a:hover {
    background:#CCC;
}
.menu a ul {
    display:none;
    position:absolute;
    left:0;
    top:1.5em;
    list-style:none;
    margin:0;
    padding:0;
    background:#EEE;
}
.menu a ul li{
    display:block;
    float:none;
}
.menu a:hover ul {
    display:block;
}

Это решение во-первых, является не валидным решением, а во-вторых, ни один современный браузер подобного сделать не даст и будет исправлять верстку до первоначального варианта. Однако с некоторыми проблемами IE6 это проглотит, правда может вылетать через некоторое время после активного пользования "таким" меню.

Обойти такую проблему можно еще более страшным изменением верстки, вложить все вложенное меню в таблицу, а уже таблицу вложить в ссылку первого уровня (^_^ знаю, pepelsbey на меня нет):
<ul class="menu">
    <li>
        <a href="#" title="Main">Main
            <table><tr><td>
                <ul>
                    <li><a href="#" title="Services">Services</a></li>
                    <li><a href="#" title="Products">Products</a></li>
                    <li><a href="#" title="Job">Job</a></li>
                </ul>
            </td></tr></table>
        </a>
    </li>
    <li>
        <a href="#" title="Portfolio">Portfolio
            <table><tr><td>
                <ul>
                    <li><a href="#" title="Projects">Projects</a></li>
                    <li><a href="#" title="Styles">Styles</a></li>
                    <li><a href="#" title="Private clients">Private clients</a></li>
                </ul>
            </td></tr></table>
        </a>
    </li>
    <li>
        <a href="#" title="FAQ">FAQ
            <table><tr><td>
                <ul>
                    <li><a href="#" title="Q&A">Q&amp;A</a></li>
                    <li><a href="#" title="Wiki">Wiki</a></li>
                </ul>
            </td></tr></table>
        </a>
    </li>
</ul>
А CSS подправив следующим образом:
{
    color:#555;
}
a:hover {
    color:#000;
}
.menu {
    list-style:none;
    margin:0;
    padding:0;
}
.menu li {
    float:left;
    margin:0;
    height:1%;
    position:relative;
}
.menu a {
    display:block;
    padding:0.2em 1em;
}
.menu a:hover {
    background:#CCC;
}
.menu a table {
    display:none;
    position:absolute;
    left:0;
    top:1.5em;
}
.menu a table ul {
    list-style:none;
    margin:0;
    padding:0;
}
.menu a ul li{
    float:none;
    display:block;
    margin:0;
    padding:0;
}
.menu a:hover table {
    display:block;
    background:#EEE;
}

Однако в результате получается, что такое "специфическое" решение нормальные браузеры и IE6 понимают и обрабатывают, а вот более IE7 и выше уже не понимают...

Последнее решение:

Идея этого решения, собственно на котором я и остановился, состоит в объединении естественного решения для нормальных браузеров и специфического решения для IE6. Такое объединение позволяет организовать механизм условных комментариев (conditional comments), поддерживаемый в браузерах Internet Explorer.

Изменяем верстку с использованием условных комментариев:
<ul class="menu">
    <li>
        <a href="#" title="Main">Main
        <!--[if gte IE 7]><!--></a><!--<![endif]-->
        <!--[if lte IE 6]><table><tr><td><![endif]-->
        <ul>
            <li><a href="#" title="Services">Services</a></li>
            <li><a href="#" title="Products">Products</a></li>
            <li><a href="#" title="Job">Job</a></li>
        </ul>
        <!--[if lte IE 6]></td></tr></table></a><![endif]-->
    </li>
    <li>
        <a href="#" title="Portfolio">Portfolio
        <!--[if gte IE 7]><!--></a><!--<![endif]-->
        <!--[if lte IE 6]><table><tr><td><![endif]-->
        <ul>
            <li><a href="#" title="Projects">Projects</a></li>
            <li><a href="#" title="Styles">Styles</a></li>
            <li><a href="#" title="Private clients">Private&nbsp;clients</a></li>
        </ul>
        <!--[if lte IE 6]></td></tr></table></a><![endif]-->
    </li>
    <li>
        <a href="#" title="FAQ">FAQ
        <!--[if gte IE 7]><!--></a><!--<![endif]-->
        <!--[if lte IE 6]><table><tr><td><![endif]-->
        <ul>
            <li><a href="#" title="Q&A">Q&amp;A</a></li>
            <li><a href="#" title="Wiki">Wiki</a></li>
        </ul>
        <!--[if lte IE 6]></td></tr></table></a><![endif]-->
    </li>
</ul>
Для современных браузеров оставляем изначальную верстку, а для IE6 переопределяем правила:
.menu li {
    position: relative;
    padding:0;
    height:1%;
}
.menu li a {
    display:block;
    padding:0.2em 1em;
}
.menu li a:hover {
    background:#CCC;
}
.menu a ul {
    background:none;
}
.menu a ul li{
    display:inline;
}
.menu a:hover ul {
    display:block;
    background:#EEE;
}
Замечу, что эти правила также подключаются к странице через условные комментарии, что избавить современные браузеры от загрузки не нужных правил.

Таким образов, получаем кроссбраузерное решение, которое можно значительно усложнить, что продемонстрировал Stu Nicholls.

Конец ретроспективе, возвращаемся в будущее! ^_^

Progg it

2 комментария:

  1. И не стыдно использовать СТОЛЬКО условных комментариев в html-коде?

    Только behaviour: url(.htc);!

    ОтветитьУдалить
  2. Не стыдно, так как это оправданно... С behavior есть серьезная зависимость от js и настроек безопасности IE.

    Большое количество условных комментариев скрашивается более универсальным решением в целом...

    ОтветитьУдалить