четверг, 18 октября 2012 г.

[вёрстка] Как сделать вкладки (табы) на чистом CSS и HTML без JavaScript

В наше время никого уже не удивишь выпадающим меню, сделанном на чистом CSS без использования JavaScript — теперь все так делают. Если же нужно оформить контент на странице в виде вкладок (табов), всё еще часто прибегают к помощи JavaScript. Однако многие и не догадываются, какие потрясающие вещи позволяет делать современный CSS. Существует несколько способов добавить на сайт табы, используя только CSS и ни строчки кода скриптов. В этой серии статей я расскажу вам о них.

Я буду писать с учётом того, что читатель не только хочет получить готовый пример кода для вставки на сайт, но и разобраться с CSS и научиться вёрстке.

Есть, как минимум, 4 метода организации вкладок на чистом CSS. Самый старый из мне известных — работает при помощи шаманства с overflow. Второй — самый удобный и технологичный (на мой взгляд) — основан на использовании свойства :checked и радио-кнопок (переключателей). Третий использует :target. Четвёртый позволяет переключать вкладки по наведению мыши, без щелчка.

В этой статье я расскажу про первый способ. Он имеет некоторые недостатки, но совместим даже со старыми браузерами. Если вам важна совместимость сайта со старыми браузерами, этот способ вам подходит. Если же вам интересен более современный и простой способ создания вкладок, смотрите мою следующую статью.

Эта статья получилась немного исторической. В примерах ниже я буду писать код так, как это делалось во времена Internet Explorer 6. Никаких новых селекторов и аттрибутов, только старые-добрые overflow, float, border, width и height. Чтобы сверстать табы, этого нам хватит. Вряд ли вам на практике придётся столкнуться с необходимостью обеспечивать совместимость с IE6, но если уж я взялся рассказать про исторический способ, буду следовать «духу эпохи». Приведённые в статье примеры проверены в IE6 и IE8, а также в современных версиях Firefox и Chrome.

Классические табы HTML

Итак, как же сделать переключение табов на CSS? Как мы помним, CSS и HTML являются чисто описательными языками. С их помощью невозможно задать какое-либо поведение, и они не имеют изменяемых состояний. А состояния нам нужны, чтобы помнить текущую вкладку.

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

Напомню вам, что такое якорь, если вы забыли или никогда не знали.

Как вы знаете, полный адрес страницы сайта обычно состоит из названия протокола связи, имени сайта и адреса страницы внутри сайта.
Например, рассмотрим такой адрес:
http://www.helpful-stuff.ru/2012/10/kak-sdelat-vkladki-na-css-bez-javascript.html

Этот адрес имеет следующие составные части:

  • http — протокол связи, используемый браузером для соединения с сайтом.
  • www.helpful-stuff.ru — имя сайта (или как еще говорят, доменное имя).
  • 2012/10/kak-sdelat-vkladki-na-css-bez-javascript.html — адрес страницы внутри сайта.

Однако мы можем указывать в ссылке не только адрес страницы, но и адрес отдельного элемента внутри страницы. В этом случае адрес выглядит следующим образом:
http://www.helpful-stuff.ru/2012/10/kak-sdelat-vkladki-na-css-bez-javascript.html#page1-2

Имя, указанное в адресе после символа решетки, называется якорем.

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

А как же нам задать идентификаторы для элемента страницы? Идентификатор для любого тега страницы можно задать при помощи свойства id.

Также мы вспоминаем, что CSS позволяет описывать контейнеры, которые по ширине и/или высоте меньше, чем содержимое контейнера. А что происходит с содержимым, выходящим за границы контейнера? Если у контейнера установлен аттрибут overflow: hidden, то такое содержимое не отображается. Получается маленькое окно, через которое нам видна часть большего региона.

Эти два факта позволяют организовать вкладочный интерфейс. В заголовках вкладок мы расположим ссылки на якори. А сами якори поместим в содержимое вкладок. Блок, внутри которого находится содержимое вкладок («окно»), ограничим в размерах так, чтобы одновременно через него была видна только одна вкладка. Работать это будет следующим образом: когда мы нажимаем на ссылку в заголовке вкладки, браузер понимает, что нужно показать нам соответствующий якорь. Он прокручивает область вкладкок так, чтобы запрашиваемый якорь оказался в «окне». Со стороны это выглядит как смена одной вкладки на другую.

HTML код для вкладок возьмём такой:

<div class="notebook" id="notebook1">
 <ul class="tabs">
  <li><a href="#page1-1">Первая</a></li>
  <li><a href="#page1-2">Вторая</a></li>
  <li><a href="#page1-3">И третья</a></li>
  <li><a href="#page1-4">Даже четвертая</a></li>
  <li><a href="#page1-5">И совсем уж пятая</a></li>
 </ul>
 <ul class="pages">
  <li class="page" id="page1-1">1</li>
  <li class="page" id="page1-2">2</li>
  <li class="page" id="page1-3">3</li>
  <li class="page" id="page1-4">4</li>
  <li class="page" id="page1-5">5</li>
 </ul>
</div>

Блок с классом notebook задаёт наш вкладочный интерфейс. Внутри находится два списка: список названий вкладок (tabs) и список с содержимым вкладок (pages). Каждая отдельная вкладка имеет класс page.

Каждая вкладка должна быть представлена уникальным идентификатором-якорем. В этом коде мы видим якори page1-1, page1-2, page1-3, page1-4, page1-5. Когда вы добавляете на свой сайт новые вкладки, не забывайте давать им новые названия якорей.

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

CSS для этого способа довольно прост. Сначала задаем базовые параметры внешнего вида:

.notebook,
.notebook .tabs,
.notebook .pages,
.notebook .page {
 margin: 0;
 padding: 0;
}

.notebook .tabs, .notebook .pages {
 list-style: none;
 overflow: hidden;
}

.notebook .tabs li {
 float: left;
}

.notebook .pages {
    clear: both;
}

Здесь мы устанавливаем overflow: hidden для контейнера pages (и для .tabs за одно, не помешает).
При помощи float: left список названий вкладок выстраивается в горизонтальную полоску.
clear: both необходим, чтобы содержимое вкладок не налезало на float-секцию, а рисовалось ниже.
list-style: none отключает у списка, собственно, внешний вид списка (убирает крупные точки, отмечающие каждый пункт).

Затем придадим нашим вкладкам привычный вид. Рисуем границы вкладок:

.notebook .tabs {
 border-left: 1px solid gray;
}

.notebook .tabs li {
 border-right: 1px solid gray;
 border-top: 1px solid gray;
}

.notebook .pages {
 border: 1px solid gray;
}

В этом фрагменте интерес представляет то, как формируется обводка заголовков вкладок. Для каждого таба мы включаем отображение правой и верхней границы. Т.к. табы состыковываются вплотную друг к другу, у нас получается правильная «табличная» обводка. Но для первого таба нам надо закрыть пустоту с левой стороны. Для этого мы включаем отображение левой границы у контейнера, в котором лежат табы. Для профи это всё очевидно, но новичкам будет полезно разобраться в этом примере. При помощи манипуляции с границами можно создавать довольно сложные схемы, особенно в новых браузерах с поддержкой CSS3.

Задаём цвета и шрифты для названий вкладок:

.notebook .tabs {
 font-family: Georgia;
 font-size: 12px;
 line-height: 30px;
 height: 30px;
}

.notebook .tabs li {
 background: #eee;
 padding: 0 10px;
 height: 30px;
}

.notebook .tabs a {
 text-decoration: none;
}


.notebook .tabs li:hover {
 background: #ddd;
}

Ну и наконец задаем фиксированные размеры для вкладок и для контейнера вкладок, что в итоге и даст нам возможность прокрутки:

#notebook1 .pages, #notebook1 .page {
 width: 500px;
 height: 200px;
}

Как видите, я использовал два селектора: #notebook1 .pages указывает на список-контейнер, в котором лежит содержимое вкладок, а #notebook1 .page указывает на отдельную вкладку внутри него. Для контейнера и для вкладки выставлен одинаковый размер. Таким образом, когда страница откроется в браузере, будет видна только первая вкладка.

Обратите внимание: в предыдущих правилах CSS я использовал имя класса notebook, а в правиле, задающем размеры — идентификатор notebook1. Дело в том, что правила для класса notebook относятся к любой панели вкладок, а правила для указания размеров — «к вот этой конкретной» панели. У вас в одном документе может использоваться несколько панелей с вкладками, и для каждой панели вы, вероятно, захотите указать собственные размеры.

Также я дополнительно выставил цвет фона и шрифт для содержимого вкладок (не буду приводить код CSS, т.к. он не относится к самому уроку). И вот что получилось:

Если вы в этом примере попереключали вкладки, то наверное уже заметили недостаток, присущий этому способу переключения вкладок. Дело в том, что если страница имеет полосу прокрутки (т.е. контента на ней больше, чем умещается на один экран по вертикали), то при переключении вкладок браузер будет стараться отмотать страницу так, чтобы верхняя граница вкладки совпала с верхней границей окна. Так действует механизм перехода по якорю. Он прокручивает наше сконструированное вкладочное «окно», но и текст в окне браузера тоже прокручивает. Получается, что при переключении вкладок страница дергается. Если же на странице нет полосы прокрутки, переключение вкладок работает идеально — никакого дергания.


Также мы можем размещать внутри вкладок другие панели вкладок. Вот в этом примере на третьей вкладке я разместил еще одну панель вкладок:



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

С другой стороны, как я уже выше отметил, если содержимое страницы не умещается на один экран, страница будет дергаться при переключении вкладок. Если вы делаете что-то типа веб-приложения, в котором возможность прокрутки страницы вообще не предполагается, то такой способ вам вполне подойдёт. Если у вас вкладки расположены в самой нижней части страницы (например, в футере под содержимым статей блога), то тоже всё нормально — дергания не будет, т.к. дальше прокручивать страницу некуда.

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

Тем не менее, существует хоть и «неправильный», но вполне рабочий способ добиться визуальной индикации текущей вкладки. У этого способа есть только одно ограничение: точная ширина заголовков вкладок должна быть нам известна заранее. (На самом деле, есть и более простые пути индикации вкладок, чем описанный ниже. Но поскольку описанный в этой статье вкладочный интерфейс рассчитан на старые браузеры, я стараюсь использовать наиболее древние приёмы CSS. Здесь использованы только float, border и указание размеров блоков — то, что работает в CSS «с самого начала времён».)

Посмотрим — что у нас изменяется при переключении вкладок? Изменяется только видимая область в прокручиваемом «окне». Значит, у нас единственный выход: элементы оформления, показывающие активную вкладку, должны быть привязаны к содержимому вкладки.

Возьмём панель с 4-мя вкладками. В начало содержимого каждой вкладки добавим div с классом page-top-border, содержащий внутри себя еще 4 тега div. (Тегов должно быть по числу вкладок. Если у вас будет 6 вкладок, ставьте по 6 тегов div.) В каждой вкладке одному из этих div пропишем класс page-active: на первой вкладке — первому div-у, на второй — второму и так далее. Такая вот простыня получается:

<div class="notebook" id="notebook4">
 <ul class="tabs">
  <li><a href="#page4-1">Первая</a></li>
  <li><a href="#page4-2">Вторая</a></li>
  <li><a href="#page4-3">Третья</a></li>
  <li><a href="#page4-4">Четвертая</a></li>
 </ul>
 <ul class="pages">
  <li class="page" id="page4-1">
   <div class="page-top-border"><div class="page-active"></div><div></div><div></div><div></div></div>
   1
  </li>
  <li class="page" id="page4-2">
   <div class="page-top-border"><div></div><div class="page-active"></div><div></div><div></div></div>
   2
  </li>
  <li class="page" id="page4-3">
   <div class="page-top-border"><div></div><div></div><div class="page-active"></div><div></div></div>
   3
  </li>
  <li class="page" id="page4-4">
   <div class="page-top-border"><div></div><div></div><div></div><div class="page-active"></div></div>
   4
  </li>
 </ul>
</div>

Эти внутренние div мы вытянем в горизонтальную линию (при помощи всё того же аттрибута float). Они будут служить границей, отделяющей заголовки вкладок от содержимого вкладок. В этом случае граница получается не непрерывной, а состоящей из отдельных сегментов, и мы можем влиять на оформление этих сегментов из CSS.

Пишем CSS. Сначала часть, привязанная к классу notebook (т.е. общая для любых панелей вкладок):

.notebook .page .page-top-border
{
 margin: 0px;
 padding: 0px;
 border: none;
}

.notebook .page .page-top-border *
{
 height: 2px;
 font-size: 1px;
 line-height: 1px;
 border-top: 1px solid gray;
 float: left;
 margin: 0px;
 padding: 0px;
}

.notebook .page-top-border .page-active
{
 border-top: 1px solid #eee;
}

Думаю, этот код понятен. Размещаем теги div в линию, задаем им высоту в 2 пикселя и тёмно-серую границу (такой же цвет, как у границ вкладок).
Для класса page-active цвет границы меняем на такой, который совпадает с фоном таба.
Аттрибуты font-size: 1px и line-height: 1px нужны для корректного отображения в IE6. IE6 интерпретирует аттрибут height не как заданную высоту, а как минимально допустимую высоту. Реальную же высоту он рассчитывает на основе размера шрифта. (Это еще незначительный такой баг, по сравнению с прочими. Про странности этого браузера сложены легенды.)

Теперь допишем стили, специфичные для конкретной панели:

#notebook4 .pages {
 border-top: none;
}

#notebook4 .pages, #notebook4 .page {
 width: 401px;
 height: 150px;
 box-sizing: border-box;
}

#notebook4 .tabs li,
#notebook4 .page-top-border * {
 width: 100px;
 box-sizing: border-box;
}

Во-первых, мы отключаем показ верхней границы для контейнера вкладок — border-top: none. Вместо этого у нас будет наша граница из отдельных сегментов.

Затем мы задаём размеры панели вкладок, ширину заголовков вкладок и сегментов границы. Важно, чтобы ширина вкладок и сегментов совпадала. Общая ширина контейнера должна быть равна ширина таба * число табов + 1. Почему добавляем один пиксель? Потому что дополнительный пиксель у нас потрачен, чтобы нарисовать самую левую границу табов — ту, которую мы задали при помощи стиля .notebook .tabs {border-left: 1px solid gray;}.

Свойство box-sizing: border-box указывает, что реальные размеры нужно считать с учетом ширины отступов и границ. Браузер IE6 всегда считает именно так, а для новых браузеров мы указываем это свойство.

Результат:



Использованный в статье код CSS полностью:

.notebook,
.notebook .tabs,
.notebook .pages,
.notebook .page {
 margin: 0;
 padding: 0;
}

.notebook .tabs, .notebook .pages {
 list-style: none;
 overflow: hidden;
}

.notebook .tabs li {
 float: left;
}

.notebook .pages {
    clear: both;
}


.notebook .tabs {
 border-left: 1px solid gray;
}

.notebook .tabs li {
 border-right: 1px solid gray;
 border-top: 1px solid gray;
}

.notebook .pages {
 border: 1px solid gray;
}


.notebook .tabs {
 font-family: Georgia;
 font-size: 12px;
 line-height: 30px;
 height: 30px;
}

.notebook .tabs li {
 background: #eee;
 padding: 0 10px;
 height: 30px;
}

.notebook .tabs a {
 text-decoration: none;
}


.notebook .tabs li:hover {
 background: #ddd;
}

#notebook1 .pages, #notebook1 .page {
 width: 500px;
 height: 200px;
}

#notebook1 .pages {
 text-align: center;
 line-height: 200px;
 font-size: 150px;
}

#page1-1 { background: #d81; }
#page1-2 { background: #8d1; }
#page1-3 { background: #1d8; }
#page1-4 { background: #18d; }
#page1-5 { background: #81d; }


#notebook2 .pages, #notebook2 .page {
 width: 320px;
 height: 320px;
}


#notebook3 .pages, #notebook3 .page {
 width: 300px;
 height: 250px;
}

#notebook3, #page2-1 img, #page2-2 img {
 padding: 5px;
}


.notebook .page .page-top-border
{
 margin: 0px;
 padding: 0px;
 border: none;
}

.notebook .page .page-top-border *
{
 height: 2px;
 font-size: 1px;
 line-height: 1px;
 border-top: 1px solid gray;
 float: left;
 margin: 0px;
 padding: 0px;
}

.notebook .page-top-border .page-active
{
 border-top: 1px solid #eee;
}


#notebook4 .pages {
 border-top: none;
}

#notebook4 .pages, #notebook4 .page {
 width: 401px;
 height: 150px;
 box-sizing: border-box;
}

#notebook4 .tabs li,
#notebook4 .page-top-border * {
 width: 100px;
 box-sizing: border-box;
}

Ну вот и всё. Экскурсия по историческим дербрям окончена. А про современный способ создания вкладок читайте следующую статью.

9 комментариев

Сергей Мазураш

Рад Вашему возвращению!

Анонимный

...а то, что страница подскакивает до уровня вкладки, это так задумано?

Анонимный

Как сделать, чтобы экран не перескакивал к началу нажатой вкладки?????

Анонимный

А внутрь вкладки кроме текста что еще можно вставить код??Виджет например???

Анонимный

А внутрь вкладки кроме текста что еще можно вставить код??Виджет например???

Анонимный

Да

Анонимный

Лойс!

Анонимный

что сделать чтоб страница не прыгала а?

Анонимный

http://shpargalkablog.ru/2012/03/css-tabs.html

Отправить комментарий