Табы на чистом CSS, от IE6+ (реинкарнация одного решения)

    Была на Хабре статья о реализации табов на чистом CSS, которую автор удалил (видимо, из-за незавершённости решения), скрыв при этом все комментарии. Благодаря авторской наработке я устранил один из недостатков юзабилити в решении и опубликовал его в комментарии. Поведение табов стало обычным, по клику на табе (или кнопке, смотря как назвать), он выделялся и оставался выделенным после отведения мыши. Чтобы не терять из виду то и другое решение и для продолжения их развития, приведу копии их. Решение автора makzimko скопировано в песочницу (IE9+), сохранив все необходимые свойства. Дополнение — в модификации там же (от IE9+). Новое (UPD3) кроссбраузерное решение с поддержкой от IE6 — на jsfiddle.net и единым файлом. Наконец, при том, что Opera 11.61 работает, для поддержки Оперы 11.50 и младше понадобилось небольшое исправление (UPD4).

    (Чем неудобен Хабр — удаление статей, сопровождающееся удалением комментариев. Нельзя быть уверенным, опубликовав комментарий, в том, что он сохранится. Теряется идея краудсорсинга — доработки сообществом изначальных сырых решений, и базы знаний — то, что написал, хранится и для себя, и для других читателей. Несколько больше уверенности придаёт статья, поэтому решение было переопубликовано в статью.)

    Основная цель обоих решений:
    Реализовать механизм табов или кнопок переключения контента, не прибегая к скриптам, без перезагрузки страницы.

    Зачем это нужно? Скорее всего, это решение будет очень кстати для страниц широкого применения, среди клиентов которых попадаются пользователи с отключенным Javascript. Чтобы не делать для них два решения — табы через сервер и табы на JS, можно сделать табы на CSS.
    Достоинства, впрочем, видны; перечислим недостатки.

    Недостатки исходного решения (часть их решена далее, часть — присуща подходу):
    *) нет памяти нажатия кнопки; :focus мог бы помочь, но любой клик по странице потеряет фокус;
    *) неподдержка IE8 и ниже (за счёт inline-block; решаемо; решено в UPD3);
    *) ограничение высоты контента — за счёт способа решения, необходимо иметь фиксированную высоту для контента, превышение которой приведёт к обрезанию содержимого. (Решение — скролл для каждого внутреннего блока.);
    *) немодульность (если не в фрейме) — нельзя использовать как независимый блок на странице, только как окно или фрейм.

    Недостатки в дополнении:
    Кроме устранения первого недостатка, сохраняются другие и добавляется ещё пара:
    *) в дополнении возможно использовать только один ряд табов или кнопок;
    *) дублирование контента табов (заголовки).

    Однако, важность первого недостатка была больше — влияла на отображение, а появившиеся недостатки скрыты от пользователя и относятся к трудностям реализации. Стало быть, получили чуть более ограниченное в реализации, но похожее на настоящие табы решение.

    Чтобы правильно смотреть на решение, в URL страницы или фрейма должен быть записан якорь, например, #tab1. Поскольку в песочнице jsfiddle.net якори не предусмотрены, используется маленький скрипт, переключающий на якорь через секунду. В действующем решении его, разумеется, не требуется.

    На каком принципе это работает


    В HTML без JS мы можем управлять показом содержимого через клики по ссылкам. Способ смены содержимого окна без перезагрузки страницы — это якоря. Страница прокручивается до якоря так, чтобы он расположился у верхнего края окна или фрейма. Всё, что ниже якоря, будет просматриваться в окне или фрейме. Это — основная идея переключения между страницами табов.

    Далее, нам нужно скрыть лишнее. В CSS есть свойство overflow: hidden; — ограничитель показа содержимого за пределами блока, и свойство height: ограничитель высоты блока. Нужно указать высоту окна, в котором мы будем наблюдать содержимое (вот оно, ограничение на показ блока — нам нужно содержимое поместить в окно или фрейм и ещё знать, какой высоты будет блок для просмотра). В примере эти 2 свойства указаны в #tabcontent{...}.

    Далее, заботимся о показе заголовка с кнопками переключения. Он не должен двигаться при переключении по якорям, поэтому находится снаружи #tabcontent, в блоке #tabs. И нужно закрывать только что нажатые кнопки изображением выбранного неактивного таба. В решении-первоисточнике закрывания не было. Чтобы были перспективы реализации на CSS1, ничем суперсовременным не нужно пользоваться. Приписываем над каждой страницей контента (#tabcontent li) дубль кнопки и изображаем её нажатой. Ставим смещение каждой кнопки вручную так, чтобы при прокрутке по якорю попала точно на позицию только что нажатой кнопки. Приписываем z-index для дубля кнопки так, чтобы она оказалась вверху. (Далее окажется, что для IE6-8 и для остальных это делается немного разными техниками.) Совмещаем кроссбраузерные свойства в едином коде.

    В принципе, можно так описать и 2, и 3 ряда табов, если мы точно рассчитаем положение дублирующей кнопки во всех рядах. Если сделать полный дубль заголовков над каждой страницей (нужный таб видимый, остальные невидимые), можно сделать и автоматически заливаемые многострочные табы. Возникает сопутствующий вопрос: красиво ли дублировать контент? Если JS отключён, то другим способом во всех браузерах мы табы не сделаем. Если не отключён, мы с помощью JS на том же решении (ведь дублировать процедуры — это же тоже некрасиво?) продублируем код на клиенте. (В примерах из статьи нет примера полного дублирования контента, это — возможность будущего развития подхода.)

    Наконец, обеспечиваем незаворачивание табов на вторую строку, потому что решение у нас однострочное. В 2 примерах выполняется разными способами. В первом (bA9mF/45) — просто свойством white-space: nowrap;, во втором (bA9mF/52) — установлением ширины заголовка, чтобы он был всегда шире всех табов, входящих в него (#tabs{min-width: 573px; *width: 573px;})

    Решение


    Решение с модификацией проверяется и готово к дальнейшим разработкам здесь: jsfiddle.net/bA9mF/45 (просмотр).
    Выглядит так (Fx8-10, Opera11.61, Chrome 16, Safari 5.02 Win):

    Показано покрытие кнопкой .pressed последней активной кнопки и реакция на наведение мыши на других кнопках.
    В Fx не срабатывает -moz-transition (исправлено в UPD2); в Safari 5.02 в данном примере не отрабатываются градиенты, поэтому выполнено равномерной заливкой (возможно, в более старшей версии нормально). Fx3.6 работает тоже, но без -moz-transition.
    Некоторые вариации шрифтов в браузерах скрыты увеличенными кнопками .pressed, но достижимо и точное пиксельное покрытие с несколько другой вёрсткой, и заодно — нормальная реализация в IE7-8. Для этого надо выстроить кнопки по float:left и зафиксировать зазоры между ними, которые сейчас немного плавают из-за inline-block.
    UPD: в ЛС сообщили, что в Safari 5.1 всё видно с градиентами в Mac и Win. Тем лучше, теперь код CSS работает с нормальной деградацией (равномерная заливка серым) для Safari 5.0. В стилях применён -webkit-linear-gradient, который введён в Chrome 10+, Safari 5.1+.
    UPD 2: По сообщению Softlink, "Транзишн работает в ФФ, но у него есть особенность: если мы меняем позиционирование, то для этого браузера надо обязательно указать начальные координаты." Таким образом, достаточно добавить позицию top:0; в #tabs li, чтобы Fx заработал (его пример здесь).

    UPD 3 22.30: Есть решение для всех браузеров. jsfiddle.net/bA9mF/52 (просмотр)
    В IE6 есть небольшая естественная деградация — кнопки активны только при непосредственном наведении на ссылку и не работают :hover (и не должны; для реализации в IE6 делают *.htc — файлы). То же самое проявляется и у IE8, если он находится в режиме Browser Mode: Compat, в этом случае у него автоматически включается Document Mode: Ie7, а не Ie8. То же — и в отдельной странице. Поэтому (это общее требование к браузеру IE) нужно убедиться, что браузер не находится в режиме совместимости. (Подробнее о режимах браузера IE см. в обсуждении этой темы с SelenIT2 в комментариях.
    Как выглядит страница и эффекты в IE6 и IE7:

    UPD 4 13.02 10.30: Opera 10.51, 11.50 — оба решения в версии bA9mF/52 не поддерживаются (прототип работает, хотя на том же принципе). Удивительно: не работает из-за строчки #tabcontent li >div{margin-top: 266px;...}, которую, если убрать, работать будет, а нужна она, чтобы во всех браузерах корректно работало без якоря. Удивительно же то, что без этой строчки везде работает корректно, и в Опере от 10.51 тоже. Быстрый фикс: jsfiddle.net/bA9mF/65/embedded/result Но у Опер (до 11.61) другая незадача: едут наложенные табы при узком окне.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 28

      +1
      UPD: в ЛС сообщили, что в Safari 5.1 всё видно с градиентами и в Линуксе, и в Win. Тем лучше, теперь код CSS работает с нормальной деградацией (равномерная заливка серым) для Safari 5.0.

      Я сообщил не про Линукс, а про Lion :)
      В Chrome 1-9, Safari 4-5 работает -webkit-gradient();
        0
        Спасибо за тест и за информацию о том, в каких версиях появилось. Впрочем, это побочный код. Наиболее ценна идея с переключением по табам от makzimko.
        0
        Добавлю в избранное, пригодится. Спасибо.
          0
          Только имейте ввиду, что в Opera 11.50 не работает. Вкладки при наведении немного двигаются, но никакой реакции на клик не происходит.
            0
            Отлично, что заметили. Никак не ожидалось подвоха в том, что везде работает. Сделал фикс в UPD4. Вроде везде работает, но надо найти исправление бага тех же немного старых Опер при узких окнах.
        • UFO just landed and posted this here
            0
            Тогда ждём от кого-либо следующего шага развития идеи. В этом — суть краудсорсинга. Теперь — очередь следующего автора :).
            Кстати, ещё одна идея. Может быть, удастся использовать min-height: 100% для полного заполнения высоты фрейма.
            0
            Пожалуйста, выложите суть. Мне совершенно неинтересны тразишны, градиенты и прочие красивости, только скелет, с выковыриванием которого я (и, скорее всего, не только я) испытываю некоторые трудности.

            И, кстати, не юзайте этот кривоработающий иетестер. В отладчике ИЕ9 есть отличная эмуляция старых баузеров (просто клацните F12 находясь на любой странице, сами всё увидете).
              0
              Я использовал исходную наработку с парой эффектов, поэтому поневоле учитывал и их при приведении вида к кроссбраузерному. В использовании красивостей есть свой смысл: сугубо технический приём лучше воспримется если представлен в красивой обёртке. Мне при доработке самому приходилось кое-что дописывать (смещения, хаки) только для того, чтобы пример смотрелся красиво. Предлагаю доработать пример или сделать совершенно новый, с чистой сутью. Песочница jsfiddle.net этому отлично способствует. Собственно, и опубликовал свою доработку только затем, чтобы интересная идея (покрытая CSS3-свойствами) не потерялась.
                +1
                > В использовании красивостей есть свой смысл: сугубо технический приём лучше воспримется если представлен в красивой обёртке.
                Вы не описали технический приём. А обертка должна быть в качестве бонуса, не более.
                  +1
                  Действительно; я не учёл, что сам прочитал ту статью, а другие не успели. Сейчас исправлюсь (в статье). Если в 2 словах, то в окне или фрейме создаются 2 элемента: список заголовков табов и список контента. По клику на заголовке таба происходит переход на участок контента по якорю. Всё, что ниже якоря, то отображается в окне/фрейме. Чтобы при этом не мешали другие участки контента (другое содержимое соседних табов), используется overflow: hidden в блоке <ul id=«tabcontent»>. Всё. Знакомиться лучше с первым примером, там нет кроссбраузерных решений, если не считать CSS3-свойств, которые не принциипиальны.
              0
              Конкретно по этой задаче, никогда не понимал её смысла.
              Зачем?
              js табы удобнее и универсальнее.
                0
                За вопрос спасибо — ответил в статье. Действительно, сначала нужно было ответить на вопрос «зачем» :).
              • UFO just landed and posted this here
                  0
                  Подтверждаю. Ссылка с результирующим кодом в IE8 Win32 XP приводит к выпадению браузера в режим IE8Compat+IE7Standarts. И ведёт себя соответственно, как IE7, т.е. высвечивается только при наведении на ссылку, а не на кнопку. То же самое — и из локального файла *.htm, и из статического с другого хостинга, если доктайп — HTML5. А что в этом странного? Совершенно логичное поведение, IE8 не знает доктайпа HTML5, поэтому выпадает в режим совместимости.
                  • UFO just landed and posted this here
                      0
                      > В режим IE8Compat его может перевести только…
                      Вот именно. У меня при открытии окна устанавливается режим Compat, и он влияет на последующее поведение при HTML5. Если все гайды вывели из учёта того, что пользователь никогда не попадает в IE8Compat, то всё правильно, этого не должно быть никогда. Но у меня, например, попадает, и у разрабочика под рукой есть эти настройки. В зависимости от того, что он поставит, так и пойдёт. Но если стоит HTML4 Strict (ну и другие, но для этого примера говорим про него), то браузер выпадет в Document Mode IE8, а не IE7, как при HTML5.

                      (Я убедительно объяснил, что эта ситуация реальна? Единственным возражением может быть «А не надо так настраивать!», но это же не аргумент. Если что-то может сломаться, его сломают и будут правы как потребители. То самое и с HTML5 в режиме Compat.)

                      Я с этими комбинациями режимов в IE(9) писал специальный скрипт на строк 25-30, который распознавал все мыслимые комбинации настроек и правильно опознавал реальный режим с учётом браузер-мод, чтобы подгрузить нужные стили. Без него тестировщики периодически жаловались, что «вот в такой-то комбинации IE9 неправильно опознаёт версию для подгрузки CSS, сделайте что-нибудь. И проблема была в ручном выставлении режимов. Кстати, надо об этом написать…
                      • UFO just landed and posted this here
                        • UFO just landed and posted this here
                            0
                            Нет, тестирую же коды по ссылке, и из локального файла file:///… с тем же содержимым, результат один. Точнее, тут чуть хитрее ведёт себя IE8. Если открыл домашнюю страницу (ya.ru, она в квирксе, а BrowserMode:Compat) в новом окне или новый таб (он в doc:ie8), то он в обоих случаях перескакивает в режим Doc:IE7. Если перед этим явно изменю документ-моду, то не перескакивает, остаётся в установленной моде (Quirks, ie7, ie8) и ведёт соответственно. Почему есть начальное перескакивание именно в doc:ie7 — не знаю, но факт, что есть.
                            • UFO just landed and posted this here
                      0
                      > он — правильный доктайп!
                      Поэтому я оговорил, что с точки зрения IE :).
                      > Видимо, очередной нелогичный баг
                      Да, что нелогичный — очевидно.

                      А вообще результат мне понравился, ещё одну итерацию до многострочных заголовков, идею которых описывал в статье — и можно применять во всех боевых режимах.
                      • UFO just landed and posted this here
                      0
                      Ссылка: jsfiddle.net/bA9mF/45/embedded/result/
                      Броузер: Opera 11.11 (хотя я думал, все еще на старой доброй десятке сижу)

                      При нажатии на табы ничего не происходит. Яваскрипт включен.
                        0
                        Да, уже обнаружили этот удивительный факт, написан и до ката, и в UPD4 есть фикс, работающий везде и как минимум в Опере 10.51.
                      • UFO just landed and posted this here
                        • UFO just landed and posted this here
                            0
                            Есть ли пример такого варианта без ограничения в высоту блока с текстом?

                            Only users with full accounts can post comments. Log in, please.