Табы (вкладки) для сайта на CSS и JavaScript – 3 способа
В этой статье рассмотрим примеры вкладок для сайта, выполненных как с использованием только CSS, так и с применением JavaScript.
Что такое вкладки (табы)?
Вкладки (табы) – это один из элементов пользовательского интерфейса, с помощью которого можно улучшить организацию контента на сайте. Осуществляют они это посредством разделения некоторого содержимого на части и показа нам в определённый момент времени только одной из них. Переход к другой части контента осуществляется посредством нажатия на соответствующий заголовок (название) вкладки.
Таким образом, табы позволяют нам очень экономично вывести много информации. Достигается это посредством того, что информация делится на значащие секции. А для показа одной секции требуется намного меньше места, чем для вывода всей информации сразу. В результате, если сделать всё точно, то пользователь будет интуитивно понимать, что и в какой вкладке находится. В конечном счёте это может сделать сайт более понятным и значительно упростить работу с ним.
В вебе, вкладки обычно реализуются посредством ссылок (<a>
) или кнопок (<button>
). При этом каждая из них связана с определённым блоком. При нажатии на определённую ссылку или кнопку все блоки с контентом скрываются, кроме одного, с которым она связана. Кроме этого, чтобы было понятно какой контент сейчас отображается, заголовок соответсвующей вкладки как-то выделяют.
Вкладки на CSS с использованием радиокнопок и :checked
Один из способов создать вкладки на CSS – это использовать радиокнопки(<input>
с type="radio"
) и CSS-селектор :checked
.
Без оформления минимальный код табов будет выглядеть следующим образом:
<style>
/* скрываем все input[type="radio"], расположенные в .tab
.tab > input[type="radio"] {
display: none;
}
/* скрываем все .tab-content */
.tab-content {
display: none;
}
/* отображаем только тот контент, который соответствует отмеченной радоикнопки */
#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
display: block;
}
</style>
<div class="tab">
<input checked id="tab-btn-1" name="tab-btn" type="radio" value="">
<label for="tab-btn-1">Вкладка 1</label>
<input id="tab-btn-2" name="tab-btn" type="radio" value="">
<label for="tab-btn-2">Вкладка 2</label>
<input id="tab-btn-3" name="tab-btn" type="radio" value="">
<label for="tab-btn-3">Вкладка 3</label>
<div class="tab-content" id="content-1">
Содержимое 1...
</div>
<div class="tab-content" id="content-2">
Содержимое 2...
</div>
<div class="tab-content" id="content-3">
Содержимое 3...
</div>
</div>
В этом варианте радиокнопки связаны с определённым <label>
. Связь элемента <label>
с <input>
организована через атрибут for
. Это действие необходимо для того, чтобы после скрытия элементов <input>
, управлять ими (устанавливать checked
) можно было через клики по <label>
.
Отображение и скрытие контента, связанного с вкладками, выполняется очень просто. По умолчанию элементы, содержащие контент, не отображаются. Показ того или иного элемента с контентом осуществляется только в том случае, если селектор в следующем правиле позволяет выбрать его:
#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
display: block;
}
Для выделения названия текущей (активной) вкладки можно использовать следующий код:
.tab > input[type="radio"]:checked + label {
color: red;
}
Здесь стилизация выбранного <label>
выполнена с использованием селектора .tab > input[type="radio"]:checked + label
. Он выбирает <label>
, который идёт сразу же после input[type="radio"]
, находящемся в состоянии checked
.
Оформить вкладки можно по-разному. В следующем примере они визуально отображаются как кнопки:
.tab {
display: flex;
flex-wrap: wrap;
}
.tab > input[type="radio"] {
display: none;
}
.tab-content {
display: none;
width: 100%;
margin-top: 1rem;
}
#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
display: block;
}
.tab > label {
display: block;
padding: 0.5rem 1rem;
cursor: pointer;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out;
text-decoration: none;
color: #0d6efd;
border: 0;
border-radius: 0.375rem;
background: 0 0;
}
.tab > input[type="radio"]:checked + label {
cursor: default;
color: #fff;
background-color: #0d6efd;
}
Вкладки с нижнем подчеркивание названий секций:
При необходимости вместо названий вкладок можно использовать хештеги:
Вкладки, которые имеют привычный вид (с использованием селектора :has()
:
Вкладки на CSS с использованием :target
Ещё создать вкладки только на CSS можно с использованием псевдокласса :target.
Без оформления минимальный код будет выглядеть следующим образом:
<style>
.tab-content {
display: none;
}
.tab-content:target {
display: block;
}
</style>
<div class="tab">
<div class="tab-content" id="content-1">
Содержимое 1...
</div>
<div class="tab-content" id="content-2">
Содержимое 2...
</div>
<div class="tab-content" id="content-3">
Содержимое 3...
</div>
<div class="tab-nav">
<a class="tab-link" href="#content-1">Вкладка 1</a>
<a class="tab-link" href="#content-2">Вкладка 2</a>
<a class="tab-link" href="#content-3">Вкладка 3</a>
</div>
</div>
Работа этих табов основана:
- на добавлении хеша к URL-адресу страницы при нажатии на вкладку (ссылку);
- на скрытии всех блоков (
display: none
) и отображении только одного их них,id
которого сейчас находится в хеше URL-адреса.
С помощью псевдокласса :target
мы можем также выбрать другие элементы, которые каким-то определённым образом связаны с ним. Например, можем это использовать, чтобы выделить активную (текущую) вкладку:
#content-1:target~.tab-nav>[href="#content-1"],
#content-2:target~.tab-nav>[href="#content-2"],
#content-3:target~.tab-nav>[href="#content-3"] {
color: red;
}
В HTML-код табов уже добавлены классы. Используя их мы можем стилизовать табы так, как нам нужно.
Пример вёрстки вкладок на Flexbox, которые имеют стандартный вид:
Пример оформления вкладок, которые имеют внешний вид кнопок:
При необходимости, их, например, можно сделать вертикальными:
Если необходимо, чтобы табы на разных классах устройств отображались по-разному, можно использовать медиа-запросы. Пример табов, у которых название вкладок на маленьких экранах отображаются горизонтально, а на остальных вертикально:
@media (min-width: 576px) {
.tab {
flex-direction: row;
}
.tab-nav {
flex-direction: column;
margin-right: 1rem;
}
}
Табы с использованием JavaScript
Сейчас рассмотрим реализацию табов на чистом JavaScript (без использования jQuery и каких-либо других библиотек). Этот вариант может потребоваться, когда мы хотим от табов больше, чем можно сделать с помощью CSS. Например, если мы хотим при открытии вкладки загружать туда контент через AJAX.
Код HTML и CSS без дополнительного оформления:
<style>
.tab-btn-active {
pointer-events: none;
color: red;
}
.tab-pane:not(.tab-pane-show) {
display: none;
}
</style>
<div class="tab" id="tab-1">
<div class="tab-nav">
<button type="button" class="tab-btn tab-btn-active" data-target-id="0">Вкладка 1</button>
<button type="button" class="tab-btn" data-target-id="1">Вкладка 2</button>
<button type="button" class="tab-btn" data-target-id="2">Вкладка 3</button>
</div>
<div class="tab-content">
<div class="tab-pane tab-pane-show" data-id="0">Содержимое 1...</div>
<div class="tab-pane" data-id="1">Содержимое 2...</div>
<div class="tab-pane" data-id="2">Содержимое 3...</div>
</div>
</div>
К вкладке, которая должна быть активной по умолчанию необходимо добавить класс tab-btn-active
. А к контенту, связанному с ней tab-pane-show
.
JavaScript:
const showTab = (elTabBtn) => {
const elTab = elTabBtn.closest('.tab');
if (elTabBtn.classList.contains('tab-btn-active')) {
return;
}
const targetId = elTabBtn.dataset.targetId;
const elTabPane = elTab.querySelector(`.tab-pane[data-id="${targetId}"]`);
if (elTabPane) {
const elTabBtnActive = elTab.querySelector('.tab-btn-active');
elTabBtnActive.classList.remove('tab-btn-active');
const elTabPaneShow = elTab.querySelector('.tab-pane-show');
elTabPaneShow.classList.remove('tab-pane-show');
elTabBtn.classList.add('tab-btn-active');
elTabPane.classList.add('tab-pane-show');
}
}
document.addEventListener('click', (e) => {
if (e.target && !e.target.closest('.tab-btn')) {
return;
}
const elTabBtn = e.target.closest('.tab-btn');
showTab(elTabBtn);
});
Если нужно программно открыть другую вкладку, то можно использовать функцию showTab()
. Ей в качестве аргумента нужно передать ту вкладку (<button>
), которую необходимо сделать активной:
// получим все кнопки
const elTabBtns = document.querySelectorAll('#tab-1 .tab-btn');
// показать первую вкладку
showTab(elTabBtns[0]);
// показать вторую вкладку
showTab(elTabBtns[1]);
// показать третью вкладку
showTab(elTabBtns[2]);
Как работает JavaScript код табов?
Центральное место в коде JavaScript занимает функция showTab()
. Она имеет параметр elTabBtn
.
Сначала внутри функции мы получаем элемент .tab
, который содержит elTabBtn
. Если вкладка уже активна, то дальше ничего не делаем и завершаем функцию с помощью инструкции return
. В противном случае получаем значение атрибута data-target-id
с помощью следующего кода:
const targetId = elTabBtn.dataset.targetId;
После этого, получаем элемент .tab-pane
, используя targetId
:
const elTabPane = elTab.querySelector(`.tab-pane[data-id="${targetId}"]`);
Затем проверяем наличие элемента elTabPane
, который мы получили в предыдущем шаге с помощью метода querySelector
. Если элемент существует, то удаляем классы tab-btn-active
и tab-pane-show
у элементов, который в данный момент их содержат:
const elTabBtnActive = elTab.querySelector('.tab-btn-active');
elTabBtnActive.classList.remove('tab-btn-active');
const elTabPaneShow = elTab.querySelector('.tab-pane-show');
elTabPaneShow.classList.remove('tab-pane-show');
В конце добавляем к элементам elTabBtn
и elTabPane
соответственно классы tab-btn-active
и tab-pane-show
:
lTabBtn.classList.add('tab-btn-active');
elTabPane.classList.add('tab-pane-show');
Обработку события click
выполняем с помощью следующего кода:
document.addEventListener('click', (e) => {
if (e.target && !e.target.closest('.tab-btn')) {
return;
}
const elTabBtn = e.target.closest('.tab-btn');
showTab(elTabBtn);
});
Примеры
1. Пример, в котором данные о последней открытой вкладки таба будем сохранять в LocalStorage, а затем использовать эту информацию при открытии страницы для переключения на неё:
const showTab = (elTabBtn) => {
// ...
if (elTabPane) {
// ...
// сохраняем состояние вкладки в LocalStorage
const tabStorage = JSON.parse(localStorage.getItem('tab') ?? '{}');
tabStorage[elTab.id] = targetId;
localStorage.setItem('tabs', JSON.stringify(tabStorage));
}
}
// ...
// восстанавливаем состояние вкладок из LocalStorage
const updateTabs = (tabStorage) => {
Object.keys(tabStorage).forEach((tabId) => {
const elTab = document.querySelector(`#${tabId}`);
const elTabBtn = elTab.querySelector(`.tab-btn[data-target-id="${tabStorage[tabId]}"]`);
showTab(elTabBtn);
});
}
const tabStorage = JSON.parse(localStorage.getItem('tabs') ?? '{}');
updateTabs(tabStorage);
2. Пример синхронизации вкладок на разных открытых страницах, относящихся к одному источнику (через LocalStorage):
window.onstorage = (e) => {
if (e.key === 'tabs') {
const tabStorage = JSON.parse(e.newValue);
updateTabs(tabStorage);
}
}
3. Пример, в котором показано как на одной странице можно вывести несколько табов с сохранением их состояний (активных вкладок) в LocalSorage:
<div class="tabs" id="tabs-1">...</div>
<div class="tabs" id="tabs-2">...</div>
<script>
class ItcTabs { ... }
// массив табов
const tabs = {};
const elTabs = document.querySelectorAll('.tabs');
const storage = localStorage.getItem('tabs');
let storageData = {};
elTabs.forEach(el => {
tabs[el.id] = new ItcTabs(el);
el.addEventListener('tab.itc.change', (e) => {
const index = +el.querySelector('.tabs__btn_active').dataset.index;
storageData[el.id] = index;
localStorage.setItem('tabs', JSON.stringify(storageData));
})
})
if (storage) {
storageData = JSON.parse(storage);
for (let key in storageData) {
if (tabs.hasOwnProperty(key)) {
tabs[key].showByIndex(storageData[key]);
}
}
}
</script>
4. Табы, содержащие видео с YouTube. При переходе на другую вкладку будет приостанавливаться воспроизведение текущего видео.
Контент вкладок:
<div class="tab" id="tab-1">
<div class="tab-nav">
<button type="button" class="tab-btn tab-btn-active" data-target-id="0">Вкладка 1</button>
<button type="button" class="tab-btn" data-target-id="1">Вкладка 2</button>
<button type="button" class="tab-btn" data-target-id="2">Вкладка 3</button>
</div>
<div class="tab-content">
<div class="tab-pane tab-pane-show" data-id="0">
<div class="iframe">
<div class="player" id="player-1" data-video-id="5NTvXKUXEX4" data-width="560" data-height="315"></div>
</div>
</div>
<div class="tab-pane" data-id="1">
<div class="iframe">
<div class="player" id="player-2" data-video-id="Cy35h6DF8hY" data-width="560" data-height="315"></div>
</div>
</div>
<div class="tab-pane" data-id="2">
<div class="iframe">
<div class="player" id="player-3" data-video-id="6tyB97J1cVA" data-width="560" data-height="315"></div>
</div>
</div>
</div>
</div>
В элементах .player
атрибут data-video-id
определяет videoId
ролика, а data-width
и data-height
- ширину и высоту iframe
.
Загрузку API IFrame Player будем выполнять асинхронно. Для этого напишем следующий код:
const elScript = document.createElement('script');
elScript.src = 'https://www.youtube.com/iframe_api';
document.head.append(elScript);
Создание <iframe>
и проигрывателя YouTube будем выполнять после загрузки кода API посредством функции onYouTubeIframeAPIReady
:
const players = {};
function onYouTubeIframeAPIReady() {
document.querySelectorAll('.player').forEach(el => {
players[el.id] = new YT.Player(el.id, {
height: el.dataset.height,
width: el.dataset.width,
videoId: el.dataset.videoId,
playerVars: {
origin: location.origin,
}
});
})
}
Приостанавливает воспроизведение видео будем посредством метода pauseVideo
, который будем выполнять при открытии вкладки (используя для этого событие tab.itc.show
):
document.addEventListener('tab.itc.show', (e) => {
const elTabPanePrev = e.detail.elTabPanePrev;
if (elTabPanePrev) {
const player = elTabPanePrev.querySelector('.player');
player ? players[player.id].pauseVideo() : null;
}
});
Добрый день! Помогите пожалуйста , не могу понять почему выдает ошибку ((
HTML: CSS: JavaScript:Привет! Поправил ошибки в JavaScript-коде:
Вопросы:
1. Как осуществить вариант №1 (radio button), если input и label все вместе спрятаны внутрь отдельного блока?
Типа:
Только без :has, который никем (почти) не поддерживается и без JS (понятное дело).
2. В варианте №2 (:target) при первичной загрузке страницы содержимое не отображается никакое)
1. Можно только с использованием :has(), как-то по-другому только на CSS не получится
2. С :target этот момент можно доделать, чтобы по умолчанию отображалась, например, 1 вкладка (пример)
Благодарю вас за подробную развернутую статью на данную тему и за максимально просто изложенный материал.
У меня вопрос следующего рода: как сделать «табы в табах»
Пример: на сайте по продаже пластиковых окон нужно вывести информацию в виде:
Табы (Уровень 1): типы конструкций, всего 4 типа конструкций;
В зависимости от типа выбранной конструкции подгрузить картинку (с этим разобрался) и блок с табами «виды комплектаций», их 7 штук
Табы (Уровень 2): виды комплектаций, всего 7 видов комплектаций.
Могли бы вы такой пример подробно рассмотреть, буду вам очень признателен.
Сделал пример, если конечно правильно понял и сделал то что нужно (правда пришлось немного JavaScript код табов изменить): перейти.
Подскажите, столкнулся с такой проблемой.
Взял пример реализации через JS.
Если кликаю на свободное место вкладки, то работает, если кликаю к примеру по тексту вкладки, то нет.
Как реализовать, чтобы по клику на любой элемент внутри родителя сработало переключение?
<img
src=«https://itchief.ru/assets/uploadify/f/8/6/f86bf8fc0c6125d9dd8f5f48a2490897s.jpg» class=«fancybox thumbnail center»>
Есть табы с категориями, в каждой из категорий есть табы с товаром, переключение между категориями и товарами написано на чистом Javascript, а теперь проблема. Необходимо чтобы по ссылке с другой страницы открывалась необходимая категория и товар. Дайте пример пожалуйста, я не силен в js((
Эта ссылка (https://itchief.ru/examples/lab.php?topic=javascript&file=tabs-08&tab=2) откроет вторую вкладку, а эта (https://itchief.ru/examples/lab.php?topic=javascript&file=tabs-08&tab=3) — третью.
Сам код тут будет простой, нужно получить значение GET-параметра tab и передать его значение функции которая переключает вкладки:
Очень интересные и полезные примеры работы скриптов, но к сожалению для решения свой задачи ничего подходящего не нашёл.
Возможно похожее есть решение с синхронизацией табов на разных страница как на примере itchief.ru/examples/lab.php?topic=javascript&file=tabs-v3, но немного не то что надо.
Александр, может Вы как мастер сможете помочь с реализацией моей задачи.
У меня на сайте есть 2 отдельный блока div.
В одном блоке содержится информация от квадроциклах, те. Есть цена и характеристики, а во втором блоке есть табы, в которых есть информация о предоставляемых услугах, в каждом табе разная услуга.
Зада состоит в том, чтобы при клике на каждом табе справа менялась цена.
Например:
Клик по табу №1 справа менялась цена в левом блоке и так при нажатии на каждый таб. пример на картинке
Буду очень признателен за помощь.
После этого добавить скрипт, который будет при переключении вкладки изменять цену:
Пример: https://itchief.ru/examples/lab.php?topic=javascript&file=tabs-09
Ребят. Использую Табы с использованием JavaScript. Главный код сначала в посте. Без дополнений.
У меня важный вопрос:
У меня табы с плеерами (iframe).
Так вот, как остановить подзагрузку и воспроизведение видео, при переходе на другую вкладку.
А то открываю другую, а на первой идет все и не останавливается. И соответственно грузятся сразу все вкладки.
Если бы разрешить загружать основную только, а остальные при переключении, останавливая первую…
Заранее благодарю, если есть решение!
Добавил в статью пример табов, содержащие YouTube видео. При переключении видео приостанавливается.
Открыть пример
Спасибо огромное!!!
Вопрос такой: не подскажите как можно задействовать табы в вопросе, когда необходимо сделать подобный блок:
— листающийся слайдер с категориями товара;
— ниже блок с первым товаром из выбранной категории товаров и в этом блоке без перезагрузки страницы можно выбрать другой товар (торговое предложение по типу SKU битрикс), с другой ценой и например расцветкой, но относящийся к основному товару.
Меняются фото, цена и описание.
Пример блока:
prnt.sc/10xv629
sklad-kuhni.moscow/
То есть в данном случае нужно категории сделать с переключением и без перезагрузки страницы, внутри категории нужно отобразить первый товар (а в нем своего рода торговые предложения, ну либо же другие товары данной категории, просто в виде иконок цветов), при переключении выбора цвета, меняется содержимое блока и тоже без перезагрузки.
Как копать в сторону аякса — это тоже на данный момент по знаниям проблематично…
Подскажите, пожалуйста, как реализовать возможность переключения сразу нескольких табов кнопкой?
Для примера: у меня 5 блоков, в каждом по 2 вкладке. Над ними 2 кнопки, которые должны переключить сразу все табы.
Можно например так (открыть пример):
Для этого необходимо (открыть пример):
1. Добавить кнопку:
2. Создать функцию getTabsPaneContent, которая должна будет возвращать то, что мы хотим увидеть в новой вкладке:
3. Получить в JavaScript саму кнопку и привязать к ней обработчик события click, который будет добавлять новую вкладку с контентом, определяемым функцией getTabsPaneContent:
Сперва, спасибо за полезную статью )
Хочу использовать вот эти вертикальные табы itchief.ru/examples/lab.php?topic=css&file=tabs-v4 <itchief.ru/examples/lab.php?topic=css&file=tabs-v4> на сайте, но не могу решить вопрос с отображением сразу контента первой вкладки. Знаю, что можно страницу itchief.ru/examples/output.php#content-1 <itchief.ru/examples/output.php#content-1> открывать, но они у меня на главной будут сразу и нужно чтобы сразу была активна первая вкладка.
Как такое можно реализовать?
Буду признателен за ответ.
А также внести необходимые изменения в CSS. Готовый пример можно посмотреть здесь.
Остальные примеры в статье разобрал, проверил на своем сайте, все работает как положено, кроме одного загадочного для меня явления))) Одни табы с одним контентом у меня на одной странице, другие с другим контентом на совершенно другой странице. Футер где скрипт, на всем сайте один и тот же. Табы работают, но допустим в хроме на одной странице с табами вкладка остаётся после F5 которую открыл, на другой local storage не срабатывает. В мозилле наоборот, та которая не работала в хроме, работает. «Мудрый» код скрипта покажу завтра
Как этот пример работает? В этом примере к табам добавляется id:
Они нужны чтобы определить какой это таб.
Далее выполняем их инициализацию следующим образом
Теперь у нас в mytabs['tabs-1'] и mytabs['tabs-2'] находятся результаты соответственно вызова $tabs для первого и второго tabs.
Сохранение данных в LocalStorage в этом примере выполняется в таком формате:
Вам нужно написать что-то подобное.
такая конструкция отлично работает, если табы на одной и той же странице. но если они расположены на разных страницах, то не работает, пробовал по разному ничего пока не получается.
Например:
Не срабатывает указанный метод.
Необходимо перейти на определенную вкладку с другой страницы.
index.html — products.html#content-2 например
itchief.ru/assets/uploadify/4/5/3/453a9690d5d48b71c6bbf050a7894874.jpg