-
Верстка табов с помощью <dl>
Когда-то возникла необходимость сделать табы на странице. Это довольно типовая задача, поэтому, чтобы не изобретать велосипед, полез в Гугл посмотреть, как народ делает. Все реализация, которые я увидел, выглядели довольно стандартно, примерно так:
<div class="tabs"> <ul class="nav"> <li>Tab 1</li> <li>Tab 2</li> <li>Tab 3</li> <li>Tab 4</li> <li>Tab 5</li> </ul> <ul class="content"> <li>Tab content 1</li> <li>Tab content 2</li> <li>Tab content 3</li> <li>Tab content 4</li> <li>Tab content 5</li> </ul> </div>
То есть два независимых списка, в одном заголовки, во втором — содержимое.
Лично меня такая структура не устраивает, главным образом из-за того, что отсутствует семантическая связь между заголовком и содержимым. Хоть я и не страдаю наркозависимостью верстать все «валидно и семантично» ради респекта и уважухи гиков, тут отсутствие четкой связи было все-таки недостатком. Во-первых, данные из бэкэкнда придется выводить в два цикла (сначала заголовки, а потом контент), а не одним. Это хоть немного, но увеличивает объем кода и явно не добавляет производительности. Во-вторых, клиентский скрипт для переключения табов тоже будет больше и сложнее, особенно с учетом того, что на странице может быть несколько блоков с табами.
Решил попробовать избавиться от этих проблем, а заодно размять мозги
При выборе структуры элементов первым делом выбор пал на <dl>-список, который как нельзя лучше подходит для этой задачи. Думаю, не я первый решил попробовать сделать табы с помощью этого списка. Вот что у меня получилось.Чтобы приведенный пример не стал очередным источником бездумного копи-пэйста, давайте рассмотрим, за счет чего это работает.
Предположим, у нас есть набор элементов, которым указан
float: left:
А что будет, если одному из элементов этой последовательности указать
float: right?
Уже неплохо: элементы, следующие за <dd>, встали ровно за предыдущими. Но нам нужно, чтобы содержимое таба растягивалось на всю ширину, поэтому прописываем этому элементу
width: 100%:
Как и ожидалось, элемент разорвал поток. Размышляем так: если элемент прижат к правому краю контейнера, значит при изменении ширины будет изменяться левый край элемента (то есть <dd> растет справа налево). Значит, нам нужно подавить влияние левого края элемента. Сделать это можно с помощью
margin-left: -100%:
Осталось сместить элемент вниз, сделать это можно с помощью
margin-top:
Вот и все. По умолчанию все <dd>-элементы скрываем с помощью
display: none, выбранному элементу говоримdisplay: block. Скрипт переключения табов на jQuery выглядит совсем уж просто:$(function(){ $('dl.tabs dt').click(function(){ $(this) .siblings().removeClass('selected').end() .next('dd').andSelf().addClass('selected'); }); });Способ получился не супергибким (есть привязка к точному размеру закладки таба), однако позволяет решать большинство подобных задач.
44 комментария

Довольно интересно, спасибо
Очень круто!
Ага а jQuery значит не оптимально используем
зачем по обработчику на каждую закладку? Лучше воспользоваться всплыванием событий:
$(function() {
$(’dl.tabs’).click(function(e) {
var target = $(e.target);
if(target.is(’dt’)) {
target.siblings().removeClass(’selected’)
.end().next(’dd’).andSelf().addClass(’selected’);
}
});
});
Так еще и возможность динамически добавлять закладки появляется и не нужны всякие там $(…).live(…)
А если внутри dt будет еще куча элементов? Все время бегать вверх по дереву?
На самом деле хоть сколько-нибудь значимые проблемы с моим примером возникнут в том случае, если табов будет несколько сотен.
Просто jQuery хранит все обработчики в отдельном объекте и по событию window.onunload начинает их все удалять, поэтому чем меньше их, тем лучше, да и как-то правильнее так
Но да, если внутри dl будет много элементов, стоит подумать, какой способ лучше.
Octane, я сам иногда использую предложенный вами способ, однако в данном конкретном случае оверхед, на мой взгляд, великоват. dl кроме dt также содержит элементы dd, размер которых может быть большим. Кроме того, мне кажется странным оптимизировать unload в ущерб runtime.
А если табы на несколько строчек должны переносится? Если много их, к примеру.
ЗЫ: Буду очень благодарен за направление туда, где посмотреть про влияния разных margin’ов, как тут например.
только подумал, что необходимо найти решение по табам ближе к маю, как оно тут уже есть
Для многострочных табов способ не подходит. Можно попробоать сделать так, чтобы все табы были в одну строку, а те, которые не помещаются, просто скрывались бы.
http://www.w3.org/TR/CSS21/box.html
Там же можно узнать, например, что процентные значения
margin-topиmargin-bottomотсчитываются не от высоты, а от ширины блока.Если я правильно понимаю, то закладки можно делать и не через
float:left, а черезdisplay:inline-block, например. В таком случае можно сделать, чтобы они не переносились (white-space:nowrap), тогда не будет наезжания. Я только не уверен, чтоfloat:rightот этого не съедет.В принципе, да. Только надо на кроссбраузерность проверить (в Fx3 все нормально, так даже лучше: нет привязки к высоте закладки).
Точно! В Опере совсем не так, вниз не съедет.
Ты это, затрахал клевые штуки придумывать. Давай уже книгу что-ли напиши
Я не знаю, что в ней писать
Предлагайте идеи.
Кстати, в можно понапихать по ссылке. Тогда не будет проблем с :hover в ИЕ и можно будет делать закладки прямо на табы.
тупо всё вместе собери и издай. для теста вполне пойдёт даже электронная версия. а там уже определишься с форматом. а вперемешку с советами пиши мысли. так объём получиться хороший.
Написать и издать книгу — большой геморрой и трата средств. Ну и я не вижу смысла продавать то, что находится в честном свободном доступе
Если уж и браться за это дело, то как минимум нужно понимать, что должно получиться в конце.
Сергей, если не трудно скажите какие книжки вы рекомендуете почитать по вёрстке?
Неа. У IE 6 и 7 (а также у IE 8 в режиме совместимости) не полная поддержка display: inline-block;. В этих браузерах inline-block будет работать только если изначально эти элементы имеют display: inline;. А dt по умолчанию имеет display: block;.
По материалам quirksmode.org.
Мне пора уже писать ответы на часто задаваемые вопросы
Сам я ни одной книжки по верстке не прочитал, но обычно говорю: читайте всё подряд. Для начинающих специалистов очень важно втягивать как можно больше информации, но с одной оговоркой: нужно всегда понимать, почему и для чего используется каждая строчка кода, каждый атрибут. Не тупо копировать решения, а именно понимать, что и зачем используется.
Еще очень помогает общение со спецами, которые уже чего-то достигли. Но тут есть шанс нарваться на какого-нибудь зазнавшегося гика. Определить их очень просто: никогда не стесняйтесь задавать вопросы, которые иногда могут поставить под сомнение авторитетность собеседника.
Например, если кто-то говорит «делай вот так», то не нужно стесняться спросить «а почему именно так?» или «а я считаю, что лучше делать вот так». Если вам попался нормальный спец, он всегда ответит, почему вы правы/не правы и приведет примеры, когда лучше один способ, а когда — второй. Если гик — то ответ будет в стиле «ты — лох, учи матчасть, а потом спорь со мной». В этом случае предлагаю сразу закончить разговор и не обращать внимания на этого человека.
Открою маленький секрет: если блочному элементу (типа <dt>) в IE указать
display:inline; zoom:1;, то вести он себя будет точно так же, как иdisplay: inline-blockСпасибо за совет. Я этого не знал. Наверно, теперь начну использовать display: inline-block;
Классная вертска
Странно, что для сброса состояния курсора ты используешь cursor:text, а не auto. Пользователям оперы будет непривычно.
Да, с auto будет правильнее. Спасибо.
Уже неоднократно пользовался этим способом, если делать полностью на флоатах иногда всплывают странные баги в ие7, природа которых мне совсем непонятна, но с инлайн-блоками всё отлично ; )
Спасибо за урок!
Здорово, на много табов один маленький скриптик универсальный…
У меня сейчас с табами завал - на каждый свой WCC и скрипт… просто капец, сплошные скрипты, блин… Только я не пойму, а как сюда можно “шоу” привязать для спец.эффектов, например, для плавного переключения или перелистывания табов, можете такой примерчик написать?
П.С. сделали бы читателям удобство - поставили бы плагин “Подписаться на комментарии”
Кто-нибудь, подскажите, пожалуйста, как правильно вставить “.fadeIn(’slow’)” - пробовал до “.removeClass(’selected’)” и после - работает только первое нажатие, потом табы не переключаются… ??? Уже все комбинации перепробовал, блин, не выходит сделать эффект затухания. В данном случае это вообще возможно или нет?
Попробую на выходных сделать скрипт затухания.
Для “.tabs dt.selected {]” достаточно “z-index:1;”, остальные индекс-поднятия не нужны - у меня в Хроме нормально работает
>>Попробую на выходных сделать скрипт затухания.
Вот скрипт не очень интересует - примеров много… Хотелось бы узнать как в данном случае какой-либо эффект прикрутить на примере. Допустим, можно воткнуть простой “.show(’slow’)” перед “.addClass(’selected’)” или любой другой… Также фильтр пробовал сюда засунуть (именно сюда, в приведенный пример” - всё работает на 1 клик, второй уже не катит.
Просто хотелось бы как-то разукрасить переключалку, а не просто переключить. Эх, жаль… а идея сначала очень понравилась - один скрипт на все таб-блоки.
Было б здорово, если б вы все-таки поставили плагин “подписаться на комментарии”, а то не удобно каждый час-два заходить и проверять
Можно сделать сколько угодно спецэффектов, другой вопрос, что стандартные заготовки из jQuery не подходят для этого, как и для других качественных анимационных эффектов.
Бился с ИЕ часа полтора, поток рвётся внутри TD, как результат первый таб наверху, остальные внизу, при переключении остальные табы один за одним смещаются наверх, лечится …блочной вёрсткой
Побочным результатом экспериментов стал кроссбраузерный аккордеон (закомментировал float и position)
Думаю использование DL-списка для сборки аккордеона самое логичное из всех
PS: Там плагин spamfree ругается, если я указываю свой ЖЖ
PPS: Нет больше не ругается, просто страница была долго открыта, куки послетали
Можете пример прислать?
Конечно Сергей, если вы имеете ввиду аккордеон, то вот
http://sibvision.ru/demos/chiko_acco/
Если по поводу проблемы в IE7, вкратце 1) была верстка 2 колонки таблицей, 2) пытался вставить табы в “TD.mainContent”, 3) не получилось, 4) переверстал в блоки, 5) заработало
CSS знаю, типовые проблемы знаю, но тут что не так с ослом по-видимому
Пример уже не буду делать, восстанавливать исходное состояние моих HTML/CSS просто долго
Спасибо, было интересно почитать
надеюсь на продолжение
Скажите пожалуйста, а возможно ли сдлеать таб с картинками кнопок вместо текста? Чтобы для каждой вкладки была своя уникальная картинка?
Можно, конечно — ставьте картинки куда надо и все должно работать
Здравсвуйте!
Чуть переделал вашу идею — http://egor.000space.com/css-tabs.html
Убрал dt/dl, поставил дивы, заголовки не фиксированной ширины, но зато сделал переключение табов через :target (css3). Правда, одна проблема осталась: если табов много, и они не влезают в строку, они некрасиво переваливаются друг через друга
Зато работает история и не нужен яваскрипт (правда Опера глючит иногда, при нажати кнопки назад не переключает таб).
Ах да, если скрывать табы не через display:hidden, а через visibility:hidden, вся конструкция становится фиксированной высоты и перестает прыгать низ страницы (на большой странице это даже наверно притормаживать может).
крутое решение.
хотел переписать то же на чистом джаваскрипте, но столкнулся со сложностями (сложные операции с домом и кросс-бразуерность).
может у кого-то есть уже варианты?
Пока нигде не видел хорошего решения чтобы вот это:
Transfer rate: 452KB/s
Local filename: /home/rpausch/raycd.m4v
Remote filename: /var/www/lectures/raycd.m4v
Duration: 01:16:27
Color profile: SD (6-1-6)
Dimensions: 320×240
выглядело вот так:
Transfer rate:452KB/s
Local filename:/home/rpausch/raycd.m4v
Remote filename:/var/www/lectures/raycd.m4v
и так далее.Пока приходится использовать список.
Млин, тэги пропали ) в общем пример c dl отсюда:
http://dev.w3.org/html5/spec/Overview.html#the-details-element
Но надо чтобы выглядело как список.