Табы (вкладки) для сайта на CSS и JavaScript – 3 способа

Табы (вкладки) для сайта на CSS и JavaScript – 3 способа
Содержание:
  1. Что такое табы
  2. Вкладки на чистом CSS
  3. Табы с использованием JavaScript
  4. Примеры
  5. Комментарии

В этой статье рассмотрим примеры вкладок для сайта, выполненных как с использованием только CSS, так и с применением JavaScript.

Что такое табы

На странице очень часто бывает необходимо вывести большое количество информации.

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

Табы (вкладки) – это как раз и есть тот элемент интерфейса, который позволяет пользователю переключаться между контентом, разбитым на несколько секций.

В вебе табы – это просто набор ссылок или других HTML элементов, которые визуально обычно оформляют в виде вкладок или группы кнопок. При нажатии ни них они включают видимость одного какого-то блока с контентом и скрывают другие.

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

Вкладки на чистом CSS

Рассмотрим несколько способов создания табов на CSS.

Первый способ построен на радиокнопках (input с type="radio") и CSS селекторе checked.

HTML и CSS код таба:

HTML
<style>
  .tabs {
    font-size: 0;
  }

  .tabs>input[type="radio"] {
    display: none;
  }

  .tabs>div {
    /* скрыть контент по умолчанию */
    display: none;
    border: 1px solid #e0e0e0;
    padding: 10px 15px;
    font-size: 16px;
  }

  /* отобразить контент, связанный с вабранной радиокнопкой (input type="radio") */
  #tab-btn-1:checked~#content-1,
  #tab-btn-2:checked~#content-2,
  #tab-btn-3:checked~#content-3 {
    display: block;
  }

  .tabs>label {
    display: inline-block;
    text-align: center;
    vertical-align: middle;
    user-select: none;
    background-color: #f5f5f5;
    border: 1px solid #e0e0e0;
    padding: 2px 8px;
    font-size: 16px;
    line-height: 1.5;
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out;
    cursor: pointer;
    position: relative;
    top: 1px;
  }

  .tabs>label:not(:first-of-type) {
    border-left: none;
  }

  .tabs>input[type="radio"]:checked+label {
    background-color: #fff;
    border-bottom: 1px solid #fff;
  }
</style>

<div class="tabs">
  <input type="radio" name="tab-btn" id="tab-btn-1" value="" checked>
  <label for="tab-btn-1">Вкладка 1</label>
  <input type="radio" name="tab-btn" id="tab-btn-2" value="">
  <label for="tab-btn-2">Вкладка 2</label>
  <input type="radio" name="tab-btn" id="tab-btn-3" value="">
  <label for="tab-btn-3">Вкладка 3</label>
  <div id="content-1">
    Содержимое 1...
  </div>
  <div id="content-2">
    Содержимое 2...
  </div>
  <div id="content-3">
    Содержимое 3...
  </div>
</div>
Табы для сайта на чистом CSS

Посмотреть

В этом варианте радиокнопки связаны с определённым label. Связь элемента label с input выполнена через атрибут for. Это действие необходимо для того, чтобы можно было скрыть элементы input, а управление ими (установку checked) выполнять через клики по элементам label.

Стилизация выбранного элемента label в этом примере выполнена с использованием селектора input[type="radio"]:checked+label. Этот селектор выбирает элемент label, который расположен сразу же после элемента input[type="radio"], находящимся в состоянии checked.

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

CSS
#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
  display: block;
}

Отобразить вкладки можно по-разному. В следующем примере они визуально отображаются как кнопки.

CSS код для создания адаптивных вкладок в виде кнопок:

CSS
.tabs>input[type="radio"] {
  display: none;
}

.tabs>input[type="radio"]:checked+label {
  background-color: #bdbdbd;
}

.tabs>div {
  /* скрыть контент по умолчанию */
  display: none;
  border: 1px solid #eee;
  padding: 10px 15px;
  border-radius: 4px;
}

/* отобразить контент, связанный с вабранной радиокнопкой (input type="radio") */
#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
  display: block;
}

.tabs>label {
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  user-select: none;
  background-color: #eee;
  border: 1px solid transparent;
  padding: 2px 8px;
  font-size: 16px;
  line-height: 1.5;
  border-radius: 4px;
  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
  margin-right: 6px;
  cursor: pointer;
  margin-bottom: 10px;
}

.tabs>label:first-of-type {
  margin-left: 0;
}
Табы для сайта на чистом CSS

Посмотреть

В этом примере вкладки визуально представлены в виде хэштегов:

CSS
.tabs>input[type="radio"] {
  display: none;
}

.tabs>input[type="radio"]:checked+label {
  font-weight: bold;
  cursor: default;
}

.tabs>div {
  display: none;
  border-top: 1px solid #eee;
  padding: 10px 15px;
}

#tab-btn-1:checked~#content-1,
#tab-btn-2:checked~#content-2,
#tab-btn-3:checked~#content-3 {
  display: block;
}

.tabs>label {
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  user-select: none;
  padding: 2px 8px;
  font-size: 14px;
  line-height: 1.5;
  transition: color 0.15s ease-in-out;
  margin-left: 6px;
  cursor: pointer;
  margin-bottom: 4px;
}

.tabs>label:first-of-type {
  margin-left: 0;
}
Табы для сайта на чистом CSS

Посмотреть

Второй способ основывается на использовании псевдокласса :target.

Пример HTML и CSS кода для создания адаптивных вкладок, механизм работы которых организован через :target:

HTML
<style>
.tabs {
  display: flex;
  flex-direction: column;
}

.tabs__links {
  display: flex;
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 10px;
  order: 0;
  white-space: nowrap;
  background-color: #fff;
  border: 1px solid #e3f2fd;
  box-shadow: 0 2px 4px 0 #e3f2fd;
}

.tabs__links>a {
  display: inline-block;
  text-decoration: none;
  padding: 6px 10px;
  text-align: center;
  color: #1976d2;
}

.tabs__links>a:hover {
  background-color: rgba(227, 242, 253, 0.3);
}

.tabs>#content-1:target~.tabs__links>a[href="#content-1"],
.tabs>#content-2:target~.tabs__links>a[href="#content-2"],
.tabs>#content-3:target~.tabs__links>a[href="#content-3"] {
  background-color: #bbdefb;
  cursor: default;
}

.tabs>div:not(.tabs__links) {
  display: none;
  order: 1;
}

.tabs>div:target {
  display: block;
}
</style>

<div class="tabs">
  <div id="content-1">
    Содержимое 1...
  </div>
  <div id="content-2">
    Содержимое 2...
  </div>
  <div id="content-3">
    Содержимое 3...
  </div>
  <div class="tabs__links">
    <a href="#content-1">Вкладка 1</a>
    <a href="#content-2">Вкладка 2</a>
    <a href="#content-3">Вкладка 3</a>
  </div>
</div>
Табы для сайта на чистом CSS

Посмотреть

Логика этих табов основана на следующих моментах. Первый момент заключается в добавлении хэша к URL-адресу страницы при нажатии на ссылку (вкладку). Второй – это стилизация элементов, выбор которых осуществляется в зависимости от хэша в URL-адресе. Выбрать элемент, идентификатор которого соответствует хэшу в URL-адресе в CSS можно выполнить посредством псевдокласса :target. С помощью него мы можем написать селектор не только для получения элемента, на который он указывает, но и для выбора других элементов, которые каким-то определённым образом связаны с ним.

Например, выбрать вкладку, которая должна быть активной для элемента на который указывает :target можно так:

CSS
.tabs>#content-1:target~.tabs__links>a[href="#content-1"],
.tabs>#content-2:target~.tabs__links>a[href="#content-2"],
.tabs>#content-3:target~.tabs__links>a[href="#content-3"] {
  background-color: #bbdefb;
  cursor: default;
}

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

Например, чтобы сделать вкладки вертикальными их код можно изменить на следующий:

CSS
.tabs {
  display: flex;
  flex-direction: column;
}

.tabs__links {
  display: flex;
  flex-direction: row;
  order: 0;
  white-space: nowrap;
  margin-bottom: 15px;
  background-color: #fff;
  border: 1px solid #e3f2fd;
  box-shadow: 0 2px 4px 0 #e3f2fd;
}

.tabs__links>a {
  display: inline-block;
  text-decoration: none;
  color: #1976d2;
  padding: 6px 10px;
  text-align: center;
}

.tabs__links>a:hover {
  background-color: rgba(227, 242, 253, 0.3);
}

.tabs>#content-1:target~.tabs__links>a[href="#content-1"],
.tabs>#content-2:target~.tabs__links>a[href="#content-2"],
.tabs>#content-3:target~.tabs__links>a[href="#content-3"] {
  background-color: #bbdefb;
  cursor: default;
}

.tabs>div:not(.tabs__links) {
  display: none;
  order: 1;
  flex-grow: 1;
}

@media (min-width: 576px) {
  .tabs {
    flex-direction: row;
  }

  .tabs__links {
    flex-direction: column;
    border: none;
    box-shadow: none;
  }

  .tabs__links>a {
    border: 1px solid #e3f2fd;
    box-shadow: 0 2px 4px 0 #e3f2fd;
    margin-bottom: 8px;
  }

  .tabs__links>a:last-child {
    margin-bottom: 0;
  }

  .tabs>div:not(.tabs__links) {
    padding-left: 15px;
  }
}

.tabs>div:target {
  display: block;
}
Табы для сайта на чистом CSS

Посмотреть

При этом табы отображаются вертикально только на больших экранах, а на маленьких (мобильных) они отображаются горизонтально. Адаптивность табов в коде реализуется с помощью медиа-запросов.

Табы с использованием JavaScript

Сейчас разберём как можно создать табы на чистом JavaScript (без использования jQuery и каких-либо других библиотек).

Способ реализации на JavaScript может потребоваться для решения задач, которые просто невозможно решить на CSS. Например, когда нужно загружать контент динамически (через AJAX) в момент открытия вкладки.

HTML и CSS:

HTML
<style>
  .tabs {
    border: 1px solid #e0e0e0;
  }

  .tabs__nav {
    display: flex;
    flex-wrap: wrap;
    list-style-type: none;
    background: #fafafa;
    margin: 0;
    border-bottom: 1px solid #e0e0e0;
  }

  .tabs__btn {
    padding: 0.5rem 0.75rem;
    text-decoration: none;
    color: black;
    text-align: center;
    flex-shrink: 0;
    flex-grow: 1;
    border: 1px solid transparent;
    cursor: pointer;
  }

  .tabs__btn_active {
    background: #e0e0e0;
    cursor: default;
  }

  .tabs__btn:not(.tabs__btn_active):hover,
  .tabs__btn:not(.tabs__btn_active):focus {
    background-color: #eee;
  }

  .tabs__content {
    padding: 1rem;
  }

  .tabs__pane {
    display: none;
  }

  .tabs__pane_show {
    display: block;
  }
</style>

<div class="tabs">
  <div class="tabs__nav">
    <button class="tabs__btn tabs__btn_active">Вкладка 1</button>
    <button class="tabs__btn">Вкладка 2</button>
    <button class="tabs__btn">Вкладка 3</button>
  </div>
  <div class="tabs__content">
    <div class="tabs__pane tabs__pane_show">
      Содержимое 1...
    </div>
    <div class="tabs__pane">
      Содержимое 2...
    </div>
    <div class="tabs__pane" id="content-3">
      Содержимое 3...
    </div>
  </div>
</div>

JavaScript (с использованием классов):

JavaScript
class ItcTabs {
  constructor(target, config) {
    const defaultConfig = {};
    this._config = Object.assign(defaultConfig, config);
    this._elTabs = typeof target === 'string' ? document.querySelector(target) : target;
    this._elButtons = this._elTabs.querySelectorAll('.tabs__btn');
    this._elPanes = this._elTabs.querySelectorAll('.tabs__pane');
    this._eventShow = new Event('tab.itc.change');
    this._init();
    this._events();
  }
  _init() {
    this._elTabs.setAttribute('role', 'tablist');
    this._elButtons.forEach((el, index) => {
      el.dataset.index = index;
      el.setAttribute('role', 'tab');
      this._elPanes[index].setAttribute('role', 'tabpanel');
    });
  }
  show(elLinkTarget) {
    const elPaneTarget = this._elPanes[elLinkTarget.dataset.index];
    const elLinkActive = this._elTabs.querySelector('.tabs__btn_active');
    const elPaneShow = this._elTabs.querySelector('.tabs__pane_show');
    if (elLinkTarget === elLinkActive) {
      return;
    }
    elLinkActive ? elLinkActive.classList.remove('tabs__btn_active') : null;
    elPaneShow ? elPaneShow.classList.remove('tabs__pane_show') : null;
    elLinkTarget.classList.add('tabs__btn_active');
    elPaneTarget.classList.add('tabs__pane_show');
    this._elTabs.dispatchEvent(this._eventShow);
    elLinkTarget.focus();
  }
  showByIndex(index) {
    const elLinkTarget = this._elButtons[index];
    elLinkTarget ? this.show(elLinkTarget) : null;
  };
  _events() {
    this._elTabs.addEventListener('click', (e) => {
      const target = e.target.closest('.tabs__btn');
      if (target) {
        e.preventDefault();
        this.show(target);
      }
    });
  }
}

Инициализация табов:

JavaScript
new ItcTabs('.tabs');
Адаптивные вкладки на CSS и JavaScript

Посмотреть

Инициализация табов на странице осуществляется посредством создания нового объекта типа ItcTabs и передаче ему в качестве аргумента CSS-селектор или DOM-элемент которой необходимо инициализировать как табы.

Если в качестве аргумента указать селектор класса, то в качестве табов будет инициализирован только первый элемент.

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

JavaScript
const tabs = document.querySelectorAll('.tabs');
for (let i = 0, length = tabs.length; i < length; i++) {
  new ItcTabs(tabs[i]);
}

Кроме этого, результат вызова new ItcTabs() можно сохранить в переменную, а затем использовать её для программного переключения вкладок.

Например:

JavaScript
const tab = new ItcTabs('.tabs');
// программно переключиться на 2 вкладку (1 – индекс вкладки, на которую нужно перейти)
tab.showByIndex(1);

Краткое описание исходного кода JavaScript

Исходный JavaScript построен на основе класса ItcTabs.

В конструкторе:

  • this._elTabs - это DOM-элемент, содержимое которого нужно инициализировать в качестве табов;
  • this._elButtons - элементы, которые представляют собой вкладки;
  • this._elPanes - элементы, содержащие контент, который необходимо переключать с помощью вкладок.

Установка обработчика для события click осуществляется в методе _events(). Данный метод вызывается в конструкторе.

Для отображения определённого контента в зависимости от нажатой вкладки выполняется с помощью метода show(). Элемент, на который нажали передаём в качестве аргумента:

JavaScript
this.show(target);

Метод showByIndex(index) предназначен для перехода на вкладку по её индексу.

Примеры

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

JavaScript
const elTab = document.querySelector('.tabs');
// инициализация elTab как табы
const tab = new ItcTabs(elTab);

const index = localStorage.getItem('tabs-index');
index > -1 ? tab.showByIndex(index) : null;

elTab.addEventListener('tab.itc.change', (e) => {
  const index = elTab.querySelector('.tabs__btn_active').dataset.index;
  localStorage.setItem('tabs-index', index);
});

2. Пример синхронизации вкладок на разных открытых страницах, относящихся к одному источнику (через LocalStorage):

JavaScript
window.onstorage = (e) => {
  tab.showByIndex(e.newValue);
};

3. Пример, в котором показано как на одной странице можно вывести несколько табов с сохранением их состояний (активных вкладок) в LocalSorage:

HTML
<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. При переходе на другую вкладку будет приостанавливаться воспроизведение текущего видео.

Табы, содержащие YouTube ролики

Открыть пример

Контент вкладок:

HTML
<div class="tabs">
  <div class="tabs__nav">
    <button class="tabs__btn tabs__btn_active">Вкладка 1</button>
    <button class="tabs__btn">Вкладка 2</button>
    <button class="tabs__btn">Вкладка 3</button>
  </div>
  <div class="tabs__content">
    <div class="tabs__pane tabs__pane_show">
      <div class="iframe">
        <div class="player" id="player-1" data-video-id="5NTvXKUXEX4" data-width="560" data-height="315"></div>
      </div>
    </div>
    <div class="tabs__pane">
      <div class="iframe">
        <div class="player" id="player-2" data-video-id="Cy35h6DF8hY" data-width="560" data-height="315"></div>
      </div>
    </div>
    <div class="tabs__pane">
      <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 будем выполнять асинхронно. Для этого напишем следующий код:

JavaScript
const elScript = document.createElement('script');
elScript.src = 'https://www.youtube.com/iframe_api';
document.head.append(elScript);

Создание <iframe> и проигрывателя YouTube будем выполнять после загрузки кода API посредством функции onYouTubeIframeAPIReady:

JavaScript
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
    });
  })
}

Приостанавливает воспроизведение видео будем посредством метода pauseVideo, который будем выполнять при переключении вкладки (используя для этого событие tab.itc.change):

JavaScript
elTab.addEventListener('tab.itc.change', (e) => {
  const paneFrom = e.detail.paneFrom;
  if (paneFrom) {
    const player = paneFrom.querySelector('.player');
    player ? players[player.id].pauseVideo() : null;
  }
});

Комментарии: 67

Ksenya
Ksenya

Добрый день! Помогите пожалуйста , не могу понять почему выдает ошибку ((

HTML:
<div class="tab_titles">
  <p class="tab_links active-link" onclick="opentab('skills')">Навыки</p>
  <p class="tab_links" onclick="opentab('experience')">Опыт</p>
  <p class="tab_links" onclick="opentab('education')">Образование</p>
</div>

<div class="tab_contents active_tab" id="skills">
  <ul>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
  </ul>
</div>
<div class="tab_contents" id="experience">
  <ul>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
  </ul>
</div>
<div class="tab_contents" id="education">
  <ul>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
    <li><span> Lorem ipsum</span><br>Html,Css</li>
  </ul>
</div>
CSS:
.tab_links::after{
  content: '';
  width: 0;
  height: 5px;
  background-color: crimson;
  position: absolute;
  left:0;
  bottom: -8px;
  transition: 0.5s ease-in-out;
}

.tab_links.active-link::after{
  width: 100%;
}

.tab_contents ul li {
  list-style: none;
  margin: 10px 0;
}
 
.tab_contents ul li span {
  color: crimson;
  font-size: 18px;
}

.tab_contents{
  display: none;
}

.tab_contents.active_tab{
  display:block;
}
JavaScript:
let tablinks = document.getElementsByClassName('tab_links');
let tabcontents = document.getElementsByClassName('tab_contents');

function opentab(tabname) {
  for (tablink of tablinks) {
    tablink.classList.remove('active-link');
  }
  for (tabcontent of tabcontents) {
    tabcontent.classList.remove('active-tab');
  }
  event.currentTarget.classList.add('active-link');
  document.getElementById(tabname).classList.add('active-tab');
}
Александр Мальцев
Александр Мальцев

Привет! Поправил ошибки в JavaScript-коде:

let tablinks = document.getElementsByClassName('tab_links');
let tabcontents = document.getElementsByClassName('tab_contents');

function opentab(tabname) {
  for (let tablink of tablinks) {
    tablink.classList.remove('active-link');
  }
  for (let tabcontent of tabcontents) {
    tabcontent.classList.remove('active_tab');
  }
  event.currentTarget.classList.add('active-link');
  document.getElementById(tabname).classList.add('active_tab');
}
TI_Eugene
TI_Eugene
Отличная статья — всё, что я искал, в одном месте и компактно.
Вопросы:
1. Как осуществить вариант №1 (radio button), если input и label все вместе спрятаны внутрь отдельного блока?
Типа:
<div class="tabs">
  <nav>
    <input type="radio" name="tab-btn" id="tab-btn-1" value="" checked>
    <label for="tab-btn-1">Вкладка 1</label>
    ...
  </nav>
  <div id="content-1">
Только без :has, который никем (почти) не поддерживается и без JS (понятное дело).
2. В варианте №2 (:target) при первичной загрузке страницы содержимое не отображается никакое)
Александр Мальцев
Александр Мальцев
Спасибо!
1. Можно только с использованием :has(), как-то по-другому только на CSS не получится
2. С :target этот момент можно доделать, чтобы по умолчанию отображалась, например, 1 вкладка (пример)
Дмитрий
Дмитрий
Здравствуйте Александр, а как реализовать такую задачку, я сделал страницу с алфавитом и брендами на базе бутстрап табов, при выборе вкладки открывается то или иное содержание, пример тут https://laraves.ru/baseshop/brand/, а как сделать так чтоб была возможность сразу выбрать несколько вкладок, допустим A и B и S и соответственно открыть их содержание, и также чтоб поочердно при клике можно было снимать выделения? штатно есть только «ИЛИ», а хотелось бы «И»
Александр
Александр
Доброго дня, Александр.

Благодарю вас за подробную развернутую статью на данную тему и за максимально просто изложенный материал.

У меня вопрос следующего рода: как сделать «табы в табах»
Пример: на сайте по продаже пластиковых окон нужно вывести информацию в виде:
Табы (Уровень 1): типы конструкций, всего 4 типа конструкций;
В зависимости от типа выбранной конструкции подгрузить картинку (с этим разобрался) и блок с табами «виды комплектаций», их 7 штук

Табы (Уровень 2): виды комплектаций, всего 7 видов комплектаций.

Могли бы вы такой пример подробно рассмотреть, буду вам очень признателен.
Александр Мальцев
Александр Мальцев
Привет! Благодарю за отзыв.
Сделал пример, если конечно правильно понял и сделал то что нужно (правда пришлось немного JavaScript код табов изменить): перейти.
Александр
Александр
Александр, благодарю за огромную работу и помощь! Вы мне очень помогли! Это именно то, что нужно, хочу спросить следующее как вкладкам 1.1...1.4 присвоить другой класс в CSS, чтобы потом навесить на них другие свойства CSS, тем самым, чтобы они визуально выглядели по другому?
Aleks
Aleks
Я пытаюсь добавить два слайдера через шорткод в разные табы. После перезагрузки один работает (группа из 5 картинок, опционально вывожу только 3 с возможностью их прокручивать), а при переходе в следующий таб второй — нет (отображается только одна картинка из группы в увеличенном виде и не реагирует прокрутка). Причём, как только меняется разрешение экрана (через responsive в инспекторе) оба слайдера начинают работать прекрасно (если быть точнее, то тот, который во втором табе), но при очередной перезагрузке опять один работает, второй — нет. В чём может быть причина?
Александр Мальцев
Александр Мальцев
Тут нужно разбираться со слайдерами, и скорее всего слайдер, который расположен во вкладке, содержимое которого сейчас не показывается, нужно активировать в момент перехода на неё.
vlad
vlad
Здравствуйте, допустим на сайте есть блок с карточками товаров. Как при нажатии на таб показывать не одну карточку, а несколько? пока только смог реализовать через дублировании html-кода, но это наверное такой себе вариант…
sprigan
sprigan
Добрый день, Александр

Подскажите, столкнулся с такой проблемой.
Взял пример реализации через JS.

Если кликаю на свободное место вкладки, то работает, если кликаю к примеру по тексту вкладки, то нет.
Как реализовать, чтобы по клику на любой элемент внутри родителя сработало переключение?

<img
src=«https://itchief.ru/assets/uploadify/f/8/6/f86bf8fc0c6125d9dd8f5f48a2490897s.jpg» class=«fancybox thumbnail center»>
Александр Мальцев
Александр Мальцев
Привет! Код JavaScript поправил в примере: tabs-v1
sprigan
sprigan
Большое спасибо!
Primerio
Primerio
Есть баг с табами, если рядом с названием вкладки будет картинка, про нажатие на нее таб не открывается, только если нажать на текст
Александр Мальцев
Александр Мальцев
Поправил код JavaScript в примере: tabs-v1
Alex Maker
Alex Maker
Здравствуйте, скажите что из выше перечисленного может помочь в решении моей проблемы?
Есть табы с категориями, в каждой из категорий есть табы с товаром, переключение между категориями и товарами написано на чистом Javascript, а теперь проблема. Необходимо чтобы по ссылке с другой страницы открывалась необходимая категория и товар. Дайте пример пожалуйста, я не силен в js((
Александр Мальцев
Александр Мальцев
Привет! Для этого можно, например, передавать в составе URL номер вкладки, которую нужно открыть:
Эта ссылка (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 и передать его значение функции которая переключает вкладки:

const tabs = $tabs('.tabs');
const urlParams = new URLSearchParams(location.search);
const tabTo = urlParams.get('tab');
if (tabTo) {
  tabs.switchTabTo(tabTo);
}
Олег
Олег
Доброго времени суток друзья.
Очень интересные и полезные примеры работы скриптов, но к сожалению для решения свой задачи ничего подходящего не нашёл.
Возможно похожее есть решение с синхронизацией табов на разных страница как на примере itchief.ru/examples/lab.php?topic=javascript&file=tabs-v3, но немного не то что надо.
Александр, может Вы как мастер сможете помочь с реализацией моей задачи.
У меня на сайте есть 2 отдельный блока div.
В одном блоке содержится информация от квадроциклах, те. Есть цена и характеристики, а во втором блоке есть табы, в которых есть информация о предоставляемых услугах, в каждом табе разная услуга.
Зада состоит в том, чтобы при клике на каждом табе справа менялась цена.
Например:
Клик по табу №1 справа менялась цена в левом блоке и так при нажатии на каждый таб. пример на картинке


Буду очень признателен за помощь.
Александр Мальцев
Александр Мальцев
Привет! Можно добавить к вкладкам data-атрибут с ценой:

<div class="tabs">
  <div class="tabs__nav">
    <a class="tabs__link tabs__link_active" href="#content-1" data-price="100">Вкладка 1</a>
    <a class="tabs__link" href="#content-2" data-price="200">Вкладка 2</a>
    <a class="tabs__link" href="#content-3" data-price="300">Вкладка 3</a>
  </div>
  <div class="tabs__content">...</div>
</div>
После этого добавить скрипт, который будет при переключении вкладки изменять цену:

const tabs = $tabs('.tabs');
function updatePrice(tabsLink) {
  const price = tabsLink.dataset.price;
  document.querySelector('.price').textContent = price;
}
updatePrice(document.querySelector('.tabs__link_active'));
document.addEventListener('tab.show', function(e) {
  updatePrice(e.detail.querySelector('.tabs__link_active'));
});
Пример: https://itchief.ru/examples/lab.php?topic=javascript&file=tabs-09
Siarhei
Siarhei
То что надо. Спасибо автору большое!
Ребят. Использую Табы с использованием JavaScript. Главный код сначала в посте. Без дополнений.

У меня важный вопрос:
У меня табы с плеерами (iframe).
Так вот, как остановить подзагрузку и воспроизведение видео, при переходе на другую вкладку.
А то открываю другую, а на первой идет все и не останавливается. И соответственно грузятся сразу все вкладки.


Если бы разрешить загружать основную только, а остальные при переключении, останавливая первую…

Заранее благодарю, если есть решение!
Александр Мальцев
Александр Мальцев
Пожалуйста!
Добавил в статью пример табов, содержащие YouTube видео. При переключении видео приостанавливается.
Открыть пример
Siarhei
Siarhei
Просто красавчик!
Спасибо огромное!!!
Александр Сидоров
Александр Сидоров
Добрый день Александр!

Вопрос такой: не подскажите как можно задействовать табы в вопросе, когда необходимо сделать подобный блок:

— листающийся слайдер с категориями товара;
— ниже блок с первым товаром из выбранной категории товаров и в этом блоке без перезагрузки страницы можно выбрать другой товар (торговое предложение по типу SKU битрикс), с другой ценой и например расцветкой, но относящийся к основному товару.
Меняются фото, цена и описание.

Пример блока:
prnt.sc/10xv629
sklad-kuhni.moscow/

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

Как копать в сторону аякса — это тоже на данный момент по знаниям проблематично…
Дмитрий
Дмитрий
Здравствуйте! Подскажите как сделать несколько блоков с табами на одной странице их мне нужно 4 (https://itchief.ru/assets/uploadify/9/8/8/9889f32d8a70fb4e1dc8bd505ace4887.jpg). Пробую вариант с отображением как кнопки. У меня только две вкладки, в каждом блоке разное содержание, название вкладок одинаковое.
Александр Мальцев
Александр Мальцев
Привет! Связывание вкладок с содержимым выполняется через id, название может быть любым. Тут всё просто, проблем не должно быть. Как сделать: пример.
Дмитрий
Дмитрий
Просто огромное спасибо, все работает как надо.
ofam
ofam
Здравствуйте.
Подскажите, пожалуйста, как реализовать возможность переключения сразу нескольких табов кнопкой?
Для примера: у меня 5 блоков, в каждом по 2 вкладке. Над ними 2 кнопки, которые должны переключить сразу все табы.
Александр Мальцев
Александр Мальцев
Привет!
Можно например так (открыть пример):
<!-- Добавить к кнопкам или ссылкам атрибут data-switch-to, в котором указать номер открываемых вкладок для табов -->
<a href="#" data-switch-to="1">1</a>
<a href="#" data-switch-to="2">2</a>

<script>
// ...
// массив, в который будем помещать табы
let tabs = [];
// переберём все табы, инициализируем их, и поместим результат в массив tabs
document.querySelectorAll('.tabs').forEach(function ($element) {
  tabs.push($tabs($element));
});

// при клике будем определять имеет ли цель data-switch-to и если да, то выполнять переключение вкладок
document.onclick = function (e) {
  if (e.target.dataset.switchTo) {
    e.preventDefault();
    // получим номер вкладки
    const index = +e.target.dataset.switchTo;
    // переберём все табы
    tabs.forEach(function ($tab) {
      // переключим вкладку на нужную
      $tab.switchTabTo(index);
    })
  }
}
</script>
Илья
Илья
А как сделать кнопку «Добавить новую вкладку»? К примеру, чтобы скопировало содержание вкладки с чистым независимым input от копируемой вкладки
Александр Мальцев
Александр Мальцев
Лучше наверно не копировать, а самостоятельно определять контент, например, с помощью функции getTabsPaneContent.
Для этого необходимо (открыть пример):
1. Добавить кнопку:
<button id="add-new-tab" type="button">Добавить новую вкладку:</button>
2. Создать функцию getTabsPaneContent, которая должна будет возвращать то, что мы хотим увидеть в новой вкладке:
<code>const getTabsPaneContent = (counter) => {
  return `<div class="tabs__pane" id="content-${counter}">
      <p>Содержимое ${counter}...</p>
      <input type="text" id="content-${counter}-input" value="">
    </div>`;
}
3. Получить в JavaScript саму кнопку и привязать к ней обработчик события click, который будет добавлять новую вкладку с контентом, определяемым функцией getTabsPaneContent:
const $addNewTab = document.querySelector('#add-new-tab');
$addNewTab.onclick = function () {
  const $tabsNav = document.querySelector('.tabs__nav');
  const counter = $tabsNav.children.length + 1;
  // вставляем новый tabs__link
  const $tabsLink = `<a class="tabs__link" href="#content-${counter}">Вкладка ${counter}</a>`
  $tabsNav.insertAdjacentHTML('beforeend', $tabsLink);
  // вставляем новый tabs__content
  const $tabsContent = document.querySelector('.tabs__content');
  $tabsContent.insertAdjacentHTML('beforeend', getTabsPaneContent(counter));
}
Ola
Ola
Супер! как всегда просто и понятно! Спасибо!
Александр Мальцев
Александр Мальцев
Пожалуйста!
Владимир
Владимир
еще есть вопросик, при нажатии на вкладку страница прокручивается к началу вкладку сверху alutat.com.ua/ а как отменить это?
Александр Мальцев
Александр Мальцев
Так вы это реализовали в виде отдельных страниц, а не на одной странице. А новая страница по умолчанию всегда открывается сначала. Чтобы это реализовать, вам нужно дополнительно добавить скрипт на страницу, который будет её прокручивать до этого состояния.
Владимир
Владимир
Здравствуйте, Александр!

Сперва, спасибо за полезную статью )

Хочу использовать вот эти вертикальные табы 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> открывать, но они у меня на главной будут сразу и нужно чтобы сразу была активна первая вкладка.

Как такое можно реализовать?

Буду признателен за ответ.
Александр Мальцев
Александр Мальцев
Здравствуйте! На сколько понимаю вы хотите без JavaScript. На CSS это можно выполнить, если только разместить содержимое вкладки, которая по умолчанию должна быть открытой, после остальных.
<div id="content-3">
  Содержимое 3...
<div>
<div id="content-2">
  Содержимое 2...
</div>
<div id="content-1">
  Содержимое 1...
</div>
А также внести необходимые изменения в CSS. Готовый пример можно посмотреть здесь.
Владимир
Владимир
супер! спасибо большое!
Nik
Nik
Доброго времени Александр! Подскажите как в данном скрипте добавить localStorage или cookie или sessionStorage, чтобы при F5 остаться на той же самой вкладке?
Александр Мальцев
Александр Мальцев
Привет! Пример как это сделать с помощью localStorage добавил в статью.
Nik
Nik
Благодарю!
Nik
Nik
А для двух табов подряд на странице не работает почему-то, код инициализации сразу всех вкладок делает нерабочим скрипт. Только если убрать один из двух табов, тогда да, сохранение в LocalStorage отрабатывает великолепно!
Nik
Nik
Разобрался, все работает!
Александр Мальцев
Александр Мальцев
Отлично!
Nik
Nik
Правда как-то очень интересно работает))) Пока еще не понял, но вроде «через раз»
Александр Мальцев
Александр Мальцев
Может что-то не учёл. Пример как это можно сделать для нескольких вкладок в статью добавил.
Nik
Nik
Сейчас проанализирую, сравню
Nik
Nik
Нашел одну причину, почему скрипт «ломало», банальная невнимательность, лишний раз обернул в дивы tabs, то, что вообще не надо было оборачивать.
Остальные примеры в статье разобрал, проверил на своем сайте, все работает как положено, кроме одного загадочного для меня явления))) Одни табы с одним контентом у меня на одной странице, другие с другим контентом на совершенно другой странице. Футер где скрипт, на всем сайте один и тот же. Табы работают, но допустим в хроме на одной странице с табами вкладка остаётся после F5 которую открыл, на другой local storage не срабатывает. В мозилле наоборот, та которая не работала в хроме, работает. «Мудрый» код скрипта покажу завтра
Nik
Nik
Код скрипта такой:
 <script>
    var $tabs = function (target) {
      var
        _elemTabs = (typeof target === 'string' ? document.querySelector(target) : target),
        _eventTabsShow,
        _showTab = function (tabsLinkTarget) {
          var tabsPaneTarget, tabsLinkActive, tabsPaneShow;
          tabsPaneTarget = document.querySelector(tabsLinkTarget.getAttribute('href'));
          tabsLinkActive = tabsLinkTarget.parentElement.querySelector('.tabs__link_active');
          tabsPaneShow = tabsPaneTarget.parentElement.querySelector('.tabs__pane_show');
          // если следующая вкладка равна активной, то завершаем работу
          if (tabsLinkTarget === tabsLinkActive) {
            return;
          }
          // удаляем классы у текущих активных элементов
          if (tabsLinkActive !== null) {
            tabsLinkActive.classList.remove('tabs__link_active');
          }
          if (tabsPaneShow !== null) {
            tabsPaneShow.classList.remove('tabs__pane_show');
          }
          // добавляем классы к элементам (в завимости от выбранной вкладки)
          tabsLinkTarget.classList.add('tabs__link_active');
          tabsPaneTarget.classList.add('tabs__pane_show');
          document.dispatchEvent(_eventTabsShow);
        },
        _switchTabTo = function (tabsLinkIndex) {
          var tabsLinks = _elemTabs.querySelectorAll('.tabs__link');
          if (tabsLinks.length > 0) {
            if (tabsLinkIndex > tabsLinks.length) {
              tabsLinkIndex = tabsLinks.length;
            } else if (tabsLinkIndex < 1) {
              tabsLinkIndex = 1;
            }
            _showTab(tabsLinks[tabsLinkIndex - 1]);
          }
        };

      _eventTabsShow = new CustomEvent('tab.show', { detail: _elemTabs });

      _elemTabs.addEventListener('click', function (e) {
        var tabsLinkTarget = e.target;
        // завершаем выполнение функции, если кликнули не по ссылке
        if (!tabsLinkTarget.classList.contains('tabs__link')) {
          return;
        }
        // отменяем стандартное действие
        e.preventDefault();
        _showTab(tabsLinkTarget);
      });

      return {
        showTab: function (target) {
          _showTab(target);
        },
        switchTabTo: function (index) {
          _switchTabTo(index);
        }
      }

    };
//Инициализация всех вкладок иначе не работает
var listTabs = document.querySelectorAll('.tabs');
for (var i = 0, length = listTabs.length; i < length; i++) {
  $tabs(listTabs[i]);
}
//Сохраняем в  LocalStorage
    var mytabs = $tabs('.tabs');
    if (localStorage.getItem('mytabs')) {
      mytabs.showTab(document.querySelector('[href="' + localStorage.getItem('mytabs') + '"]'));
    }

    document.addEventListener('tab.show', function (e) {
      localStorage.setItem('mytabs', e.detail.querySelector('.tabs__link_active').getAttribute('href'));
    })
    
  </script>
Александр Мальцев
Александр Мальцев
Так корректно работать не будет. В mytabs будет находится результат вызова функции $tabs('.tabs') для первого таба. Кроме этого в этом случае в LocalStorage нужно сохранять не просто href ссылку, а, например, их в виде некоторого объекта, как в этом примере.
Как этот пример работает? В этом примере к табам добавляется id:
<div class="tabs" id="tabs-1">...</div>
<div class="tabs" id="tabs-2">...</div>
Они нужны чтобы определить какой это таб.
Далее выполняем их инициализацию следующим образом
var
  mytabs = {
    'tabs-1': $tabs('#tabs-1'),
    'tabs-2': $tabs('#tabs-2')
  },
  ...
Теперь у нас в mytabs['tabs-1'] и mytabs['tabs-2'] находятся результаты соответственно вызова $tabs для первого и второго tabs.
Сохранение данных в LocalStorage в этом примере выполняется в таком формате:
{"tabs-2":"#content-6","tabs-1":"#content-2"}
Вам нужно написать что-то подобное.
Nik
Nik
Наверно я что-то не недопонимаю, когда мы вызываем функцию $tabs('.tabs') и происходит инициализация определенного id с помощью:
var
  mytabs = {
    'tabs-1': $tabs('#tabs-1'),
    'tabs-2': $tabs('#tabs-2')
  }
такая конструкция отлично работает, если табы на одной и той же странице. но если они расположены на разных страницах, то не работает, пробовал по разному ничего пока не получается.
Александр Мальцев
Александр Мальцев
В этом случае его можно сделать более динамичным (пример обновил в статье). Теперь он будет сохранять состояния в LocalStorage и восстанавливать открытые вкладки только для тех табов, которые имеют id.
Nik
Nik
Да, теперь табы работают на любых страницах, спасибо за наглядный пример!
Vlad
Vlad
как во втором способе сделать, чтобы первая вкладка была уже активной?
Александр Мальцев
Александр Мальцев
Для этого нужно добавить к URL страницы соответствующий хэш.
Например:
// content-1 - хэш URL
https://myblog.ru/post-7#content-1
Петр
Петр
Александр помоги.
Не срабатывает указанный метод.
Необходимо перейти на определенную вкладку с другой страницы.
index.html — products.html#content-2 например
Александр Мальцев
Александр Мальцев
А вы их как выполнили? С использованием псевдокласса :target или с использованием JavaScript. Если они у вас организованы на JavaScript, то это необходимо дополнительно прописать в коде.
Петр
Петр
С использованием javascripta. В ie 11 он не работает, даже разрешая заблокированные сценарии.
Vlad
Vlad
не так подключаю что ли на вордпрессе? кроме первой вкладки не открываются.
itchief.ru/assets/uploadify/4/5/3/453a9690d5d48b71c6bbf050a7894874.jpg
Александр Мальцев
Александр Мальцев
Необходимо js-файл подключать после HTML-кода вкладок или поместить код в обработчик события DOMContentLoaded, чтобы он выполнился после загрузки DOM-документа:
document.addEventListener('DOMContentLoaded', function() {
  // ...
});
Vlad
Vlad
Здорово сработало )
Vlad
Vlad
Здравствуйте, автор! А вы платные услуги не оказываете? Мне нужно сделать табы, и внутри у каждого карусель.
Александр Мальцев
Александр Мальцев
Здравствуйте! Карусель можно взять отсюда, после этого нужно будет просто вставить карусель в каждую вкладку.
Nik
Nik
Александр подскажите а как использовать табы два раза подряд на одной странице? Те которые с использованием java script. Сделал форму авторизации/регистрации во всплывающем окне, разместил в хэдере, форма с применением табов. Соответственно размещая табы уже в теле, получается они используются два раза подряд. И не работают которые вторые, после хэдера. Пробовал менять id, не выходит. Похоже для одновременного использования скрипт должен быть другой…
Nik
Nik
Не внимательно прочёл статью до конца, все работает, все табы на странице!
var listTabs = document.querySelectorAll('.tabs');
for (var i = 0; i <= listTabs.length; i++) {
  tabs(listTabs[i]);
} 
 
Nik
Nik
Отличные табы! Уже применил для авторизации, и в личном кабинете. Все работает супер, Спасибо!
Александр Мальцев
Александр Мальцев
Рад, что понравились!