18 сентября 2010 г.

Вертикальные вкладки

В данной статье я подробно рассмотрю процесс предоставления информации на странице, с помощью вкладок. А конкретнее, речь будет идти о, не совсем стандартных, но очень удобных, вертикальных вкладках.





ВкладкиW - очень удобное средство навигации и предоставления информации на сайте. Сам часто удивлялся на сколько удобно пользоваться сайтами, разделы которых предоставлены в виде вкладок. Секрет успеха этого "компонента" просто - "Вкладки самоочевидны и дают ощущение физического пространства", сказал Стив Круг в своей знаменитой книге "... не заставляйте меня думать". Вопросу вкладок посвящены 87-94 страницы. Настоятельно рекомендую ознакомиться с данной книгой автора, уверен, так или иначе она будет вам полезна.

Теперь о реализации. Есть множество способов быстро создать вкладки, в основном они отличаются в зависимости начального подхода к написанию сайта. Вот примеры некоторых реализаций:

Но я оттолкнусь от более простого варианта - каркас на HTML+CSS, механика на JavaScript (да, здесь без клиентских скриптов не обойтись). В таком случае можно будет глубже понять и прочувствовать механику работы и реализацию подобных элементов интерфейса.
Для упрощения работы с js скриптами, я буду использовать известную библиотеку jQuery.


Каркас и раскраска


Для начала, набросаем скелет будущей панели с вкладками.
Рассмотрев прототип будущей страницы, можно сказать, что основных составляющих будет две - корешок вкладки и её содержимое. В выбранном ключе реализации они могут находиться где-угодно на странице и как-угодно удалённые друг от друга. Я хочу сделать вкладки с вертикально расположенными корешками. Корешками будут картинки.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <title>"CSS : Неизвестное об известном", часть 5, вертикальные вкладки</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
        <div id="content">
            <div id="tab-container">
                <div style="display: block;" id="general">
                    <pre>Задумались ли вы в своей работе, ...</pre>
                </div>
                <div style="display: none;" id="participants">
                    <pre>Ах, друг мой, молодость тебе нужна, ...</pre>
                </div>
                <div style="display: none;" id="feedbacks">
                    <pre>Пергаменты не утоляют жажды. ...</pre>
                </div>
            </div>
            <div id="tab-headers">
                <ul>
                    <li><a href="#general"><img src="process.png" class="sidebar-button-icon" alt="Training Types" /></a></li>
                    <li><a href="#participants"><img src="users.png" class="sidebar-button-icon" alt="Training Types" /></a></li>
                    <li><a href="#feedbacks"><img src="comments.png" class="sidebar-button-icon" alt="Training Types" /></a></li>
                </ul>
            </div>
        </div>
    </body>
</html>

В теле каждой "будущей" вкладки, я написал отрывки из "Фауста".

Далее можно приступать к раскрашиванию макета CSS-ом.
<link href="style.css" rel="stylesheet" type="text/css" />
- добавить в тег title.

Для начала, необходимо расставить по местам главные блоки tab-container и tab-headers.
#tab-container {
    float: left;
    margin-right: -80px;
    width: 100%;
}

#tab-headers {
    float: right;
    width: 80px;
}
для "резиновой" разметки сайта и прибивания дива к правому краю, используются отрицательный правый отступ, величиной с ширину правого div'а, 80px.

Скрыть содержимое всех вкладок, кроме первой: <div style="display: block;" и <div style="display: none;" соотв.

Далее следует обратить внимание на организацию закладок, выполненых в виде списка ul-li тегами, следуя семантической вёрстке.

Посмотрим на CSS-оформление заголовков вкладок
#tab-headers > ul {
    list-style: none;
    padding: 0px;
    margin-top: 20px;
}
list-style: none отключает рисование маркеров напротив элементов списка.
вертикальный отступ в 20 px, имеет отношение лишь к общему оформлению.

#tab-header > ul > li > a {
    display: block;
}

#tab-header > ul > li > a:focus {
    outline: 0;
}
Для того что бы ссылка занимала всё пространство тега li. И для того, что бы при получении фокуса, некоторые сообразительные браузеры не рисовали точечную обводку ссылки.

Определимся с цветовым оформлением. Будем использовать в основном два цвета, #EEEEEE - для основного фона, #444444 - для фона вкладок.
А при наведении мыши на вкладку, она подсвечивается основным фоновым цветом. Текущая выбранная вкладка то же. Но для того, что бы как-то различать и управлять текущей вкладкой - выделим класс selected. Тогда:
#tab-headers > ul > li.selected,
#tab-headers > ul > li:hover {
    background-color: #EEEEEE;
}

а в селектор #tab-headers добавим background-color: #444444;
и
#content {
    background-color: #EEEEEE;
}

Осталось описать стиль для пиктограмм и текста на теле вкладок
img.sidebar-button-icon {
    border: none;
    margin: 8px;
}

#tab-container pre {
    font-size: 1em;
    font-family: sans serif, arial;
    margin-left: 20px;
}
и можно смотреть на результат.


Доработка JavaScript-ом


Далее очень просто, при помощи jQueryW библиотеки расширения JavaScriptW, можно заставить работать вкладки.
<script src="jquery.js" type="text/javascript" charset="utf-8"></script>
или
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>

и напишем простенький скрипт:
<script type="text/javascript">
    $(document).ready(function(){
        var tabContainers = $('#tab-container > div');
        tabContainers.hide().filter(':first').show();

        $('#tab-headers ul li a').click(function () {
            tabContainers.hide();
            tabContainers.filter(this.hash).show();
            $('#tab-headers ul li').removeClass('selected');
            $(this).parent().addClass('selected');
            return false;
        }).filter(':first').click();
    });
</script>
С начала определяем tabContainer, как #tab-container > div, и показываем, из всего набора div-ов, первый. Далее вешаем на #tab-headers ul li a обработчик события onClick. Он будет, с начала прятать все табы, а потом показывать тот, на который ведёт вкладка (они связаны одинаковыми id и якорем). Потом всё просто: убираем со всех #tab-headers ul li, класс selected, и ставим его на li текущей вкладки. Ну и в конце "как бы" нажимаем на первую вкладку (для того, что бы при загрузке всегда была выбрана первая вкладка).

Смотрим на результат.

В общем-то получилось то, что хотелось. Но чего-то не хватает... Как по мне, то теней под выбранной вкладкой.


Добавление тени


Опишем класс, который поможет это реализовать:
#tab-headers > ul > li.selected + li {
    box-shadow: inset 0 10px 8px -8px #000000;
    -moz-box-shadow: inset 0 10px 8px -8px #000000;
    -webkit-box-shadow: inset 0 10px 8px -8px #000000;
}
здесь использован box-shadow из спецификации CSS3.
inset
указывает, что мы описываем внутреннюю тень;
0
смещение по оси X;
10px
смещение по оси Y;
8px
плотность размытия тени;
-8px
увеличение тени (в нашем случае - уменьшение) относительно размеров самого объекта.
И получим практически то, что хотели, но с двумя проблемами:
  1. при наведении на вкладку с тенью, тень остаётся (смотрится некрасиво).
  2. при выборе последней вкладки, под нею тень не появляется.

Первая проблема решается отключением тени при li.selected + li:hover:
#tab-headers > ul > li.selected + li:hover {
    box-shadow: none;
     -moz-box-shadow: none;
     -webkit-box-shadow: none;
}

А для решения второй, прийдётся пойти на некоторую хитрость. Добавим в конец ul, ещё один замыкающий пустой li элемент. И назначим ему класс tab-last.
<li class="tab-last">&nbsp;</li>
Опишем поведение класса:
#tab-headers > ul > li.tab-last:hover {
    background-color: inherit;
}
Внимание! описание класса tab-last, необходимо сделать после li:hover.

Вот практически всё. Осталась одна мелкая недоработка, при наведении мыши на тень последней вкладки (которой является tab-last), она исчезает.
Исправляется она изменением селектора
#tab-headers > ul > li.selected + li:not(tab-last):hover {
    box-shadow: none;
     -moz-box-shadow: none;
     -webkit-box-shadow: none;
}
Теперь тень будет убираться под элементом списка, если он следует за текущим выбранным (класс selected) и при этом не является последним (псевдо-элементом).

И конечный вариант.

5 комментариев:

  1. Не получается почему-то изменить цвет фона нигде, размер блоков. Подскажите пожалуйсто, почему так?

    ОтветитьУдалить
  2. Ну как же я вам так сходу помогу?
    показывайте свой код, что там у вас.

    ОтветитьУдалить
  3. Сделал все так как описано, но проблема при клике по вкладкам контент не меняется, тупо контент с првой вкладки везде.

    ОтветитьУдалить
  4. Вот посмотрите http://skriptt.blogspot.com/2012/06/1-2-3-1-2-3.html#general

    ОтветитьУдалить
    Ответы
    1. А где же ваш джаваскрипт? Собственно, он же и анимирует это всё дело.
      см. раздел "Доработка JavaScript-ом" на этой странице.

      Удалить