Прості адаптивні вкладки (таби) для сайту на JavaScript і CSS

15

Від автора: у даній статті ми розглянемо, як зробити макет з декількох адаптивних вкладок (табів) для сайту на JavaScript і CSS 3, який просто прекрасно підходить в тих випадках, коли необхідно вмістити багато пов’язаного вмісту в маленькій і компактною області.

Прості адаптивні вкладки (таби) для сайту на JavaScript і CSSПрості адаптивні вкладки (таби) для сайту на JavaScript і CSS

Часто вкладки можна зустріти в блогах у вигляді віджетів сайдбаров, у яких, зазвичай, відображаються останні або улюблені пости, або категорії. Але крім другорядної ролі вкладок (табам) також відводять головну роль на сторінках. На щастя створювати вкладки (таби) для сайту не таке складне заняття. У цій статті ми розглянемо все, що необхідно для їх створення, навіть подбаємо про застарілих браузерах з вимкненим JS. Давайте розберемо структуру і розмітку вкладок.

Структура та розмітка

Отже, наша структура включає в себе:

Секцію меню з вкладками. Активна вкладка підсвічується

І самі вкладки, які або приховані, або видно

Вся конструкція розміщується в головний DIV, який буде використовуватися в JS для знаходження дочірніх елементів. Почнемо розмітку:

Значення для ідентифікатора можна взяти будь-яке, воно стане в нагоді пізніше в JavaScript. Також зверніть увагу на клас no-js, його ми будемо видаляти перед маніпуляціями з JS. Якщо ви працювали з бібліотеками з методом виявлення підтримуваних властивостей, як Modernizr, то вам це буде знайомо. Додамо меню:

Я відразу зробив першу вкладку активної, перший індекс в JavaScript буде 0. Зверніть увагу на те, що кількість вкладок може змінюватися, але обов’язково необхідно, щоб кількість табів і посилань на них було рівним. Перейдемо до вкладок: кожна вкладка це окремий DIV. У першого блоку одразу доданий клас is active. Кінцева розмітка виглядає так:

На даному етапі в браузері буде просто купа елементів в одному ряду, але ми це виправимо в CSS.

Додаємо стилі CSS

Зауваження: я використовую flexbox, для автоматичної генерації вендорних префіксів autoprefixer з Gulp. В CSS нижче я не писав вендорные префікси. Якщо вам потрібна готова версія, вона є на GitHub. Блоки c-tabs як таких стилів не вимагають, так що почнемо з меню. Меню у нас складається з набору посилань, нижче я додав трохи стилів:

.c-tabs-nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.c-tabs-nav__link {
flex: 1;
margin-right: 4px;
padding: 12px;
color: #fff;
background-color: #b3b3b3;
text-align: center;
transition: color 0.3 s;
}
.c-tabs-nav__link:last-child {
margin-right: 0;
}
.c-tabs-nav__link:hover {
color: #6d6d6d;
}
.c-tabs-nav__link.is-active {
color: #dc446e;
background-color: #e7e7e7;
}
.c-tabs-nav__link i,
.c-tabs-nav__link span {
margin: 0;
padding: 0;
line-height: 1;
}
.c-tabs-nav__link i {
font-size: 18px;
}
.c-tabs-nav__link span {
display: none;
font-size: 18px;
}
@all media and (min-width: 720px) {
.c-tabs-nav__link i {
margin-bottom: 12px;
font-size: 22px;
}
.c-tabs-nav__link span {
display: block;
}
}

Пройдемося по стилям меню. Основний контейнер це елемент flexbox з класом c-tabs-nav. Кожна посилання це гумовий елемент з класом c-tabs-nav__link, який розтягується так, щоб вмістити всі елементи з невеликими відступами між посилань. На кожну посилання вішається подія кліка для перемикання між вкладками. Також посилання будуть змінювати свій стан в залежності від того, навели на них мишку або кликнули. У демо у кожній посилання своя іконка і текст в тегах I і u відповідно. На маленьких екранах текст у span ховається для економії місця. Зробити це можна простим медіа запитом, який ми додаємо в кінець файлу.

Тепер розглянемо стилі для конкретної вкладки:

**
* Tab
*/
.c-tab {
display: none;
background-color: #e7e7e7;
}
.c-tab.is-active {
display: block;
}
.c-tab__content {
padding: 1.5 rem;
}

Ну тут все просто і інтуїтивно зрозуміло, але все ж таки пройдемося поетапно. Кожному табу присвоєно клас c-tab, всі вкладки заховані за замовчуванням (display: none). Якщо до нього додається клас is-active, то значення властивості display змінюється на block. Клас c-tab__content відповідає за область контенту всередині вкладки, функціонального навантаження вона не несе. Хоча через нього можна напряму звертатися до вкладках, не чіпаючи основний контейнер.

На даному етапі у вас на екрані вже повинно бути щось привабливе. Але по кліку по посиланнях меню нічого не відбувається! Без паніки, зараз виправимо.

Вдихнемо життя за допомогою JS

Давайте поетапно розпишемо, чого ми хочемо досягти з допомогою JS:

Нам необхідно якимось чином знайти компоненти, щоб потім їх налаштувати

Потрібно ініціалізувати компоненти

Необхідно перевірити, чи на всі вкладки навішені оброблювачі події

Нам потрібен метод для переходу до зазначеної вкладці, який можна викликати як всередині, так і з розмітки

І в кінці кінців, потрібно отримати доступ до наших компонентів, тобто потрібно додати в глобальний простір імен

Доручимо цю справу IIFE:

(function() {
‘use strict’;
})();

Тепер налаштуємо нашу функцію і зробимо її глобальною:

(function() {
‘use strict’;
var tabs = function(options) {
// код…
};
window.tabs = tabs;
})();

Зверніть увагу, що функція tabs приймає один аргумент option. Додамо пару змінних в кеш, зробивши їх доступними для наступної нашої функції:

(function() {
‘use strict’;
var tabs = function(options) {
var el = document.querySelector(options.el);
var tabNavigationLinks = el.querySelectorAll(options.tabNavigationLinks);
var tabContentContainers = el.querySelectorAll(options.tabContentContainers);
var activeIndex = 0;
var initCalled = false;
};
window.tabs = tabs;
})();

Ми ініціалізувати п’ять змінних:

El – містить елемент, знайдений по селектору через option

tabNavigationLinks – колекція посилань меню з певним класом, отриманих через option

tabContentContainers – контент вкладок з певними класами, отриманий через option

activeIndex – індекс активної вкладки, за замовчуванням дорівнює 0

initCalled – логічне значення, за замовчуванням дорівнює false. Використовується для перевірки, викликалася функція init, т. к. більше одного разу дану функцію викликати не треба.

Подумаємо далі. Нам потрібна якась функція, яка буде пробегаться по всіх посиланнях вкладок і вішати на них обробники подій, а також видаляти клас no-js. І, природно, потрібна функція для обробки цих подій. І, нарешті, потрібна функція, яка буде перемикати нас на задану вкладку за її індексу, яка буде викликатися з функції обробника. Забігаючи наперед, нам потрібно зробити так, щоб функцію ініціалізації і функцію щодо переходу на конкретну вкладку можна було викликати в будь-якому місці. Завдяки нашому JS і змінної initCalled зі значенням за замовчуванням false, ми можемо швидко повернутися з будь-якої функції назад в инициализатор. Нижче представлений код:

(function() {
‘use strict’;
var tabs = function(options) {
var el = document.querySelector(options.el);
var tabNavigationLinks = el.querySelectorAll(options.tabNavigationLinks);
var tabContentContainers = el.querySelectorAll(options.tabContentContainers);
var activeIndex = 0;
var initCalled = false;
var init = function() {
};
var handleClick = function(link, index) {
};
var goToTab = function(index) {
};
return {
init: init,
goToTab: goToTab
};
};
window.tabs = tabs;
})();

Давайте пройдемося по функції init. Нам потрібно, щоб ця функція викликалася раз, якщо значення initCalled одно false. Тепер нам треба переключити логічне значення і видалити клас no-js. Після цього необхідно пройти в циклі по всіх посиланнях меню і прикріпити до них обробника події за допомогою функції handleClick, подавши на вхід функції саму посилання і індекс. Нижче код:

var init = function() {
if (!initCalled) {
initCalled = true;
el.classList.remove(‘no-js’);
for (var i = 0; i < tabNavigationLinks.length; i++) {
var link = tabNavigationLinks;
handleClick(link, i);
}
}
};

Перейдемо до функції handleClick, тут все просто. До ссылке необхідно додати обробник події click і викликати функції goToTab, передавши її значення індексу. Нижче код:

var handleClick = function(link, index) {
link.addEventListener. (‘click’, function(e) {
e.preventDefault();
goToTab(index);
});
};

Нам залишилася одна функція goToTab. Марно запускати, якщо не будуть виконані 3 умови:

Поданий у функцію індекс !== індексом поточної активної вкладки

Індекс менше 0

Індекс більше найвищого значення

Якщо всі умови виконуються, нам просто потрібно переключити класи в посиланнях меню і вкладках, а також оновити значення activeIndex. Нижче код:

var goToTab = function(index) {
if (index !== activeIndex && index >= 0 && index <= tabNavigationLinks.length) {
tabNavigationLinks[activeIndex].classList.remove(‘is active’);
tabNavigationLinks[index].classList.add(‘is active’);
tabContentContainers[activeIndex].classList.remove(‘is active’);
tabContentContainers[index].classList.add(‘is active’);
activeIndex = index;
}
};

Круто, тепер все має запрацювати! Нижче повний код JS:

(function() {
‘use strict’;
/**
* tabs
*
* @description The Tabs component.
* @param {Object} options The options hash
*/
var tabs = function(options) {
var el = document.querySelector(options.el);
var tabNavigationLinks = el.querySelectorAll(options.tabNavigationLinks);
var tabContentContainers = el.querySelectorAll(options.tabContentContainers);
var activeIndex = 0;
var initCalled = false;
/**
* init
*
* @description Initializes the component by removing the no-js class from
* the component, and attaching event listeners to each of the nav items.
* Returns nothing.
*/
var init = function() {
if (!initCalled) {
initCalled = true;
el.classList.remove(‘no-js’);
for (var i = 0; i < tabNavigationLinks.length; i++) {
var link = tabNavigationLinks;
handleClick(link, i);
}
}
};
/**
* handleClick
*
* @description Handles click event listeners on each of the links in the
* tab navigation. Returns nothing.
* @param {HTMLElement} link The link to listen for events on
* @param {Number} index The index of that link
*/
var handleClick = function(link, index) {
link.addEventListener. (‘click’, function(e) {
e.preventDefault();
goToTab(index);
});
};
/**
* goToTab
*
* @description Goes to a specific tab based on index. Returns nothing.
* @param {Number} index The index of the tab to go to
*/
var goToTab = function(index) {
if (index !== activeIndex && index >= 0 && index <= tabNavigationLinks.length) {
tabNavigationLinks[activeIndex].classList.remove(‘is active’);
tabNavigationLinks[index].classList.add(‘is active’);
tabContentContainers[activeIndex].classList.remove(‘is active’);
tabContentContainers[index].classList.add(‘is active’);
activeIndex = index;
}
};
/**
* Returns init and goToTab
*/
return {
init: init,
goToTab: goToTab
};
};
/**
* Attach to global namespace
*/
window.tabs = tabs;
})();

Простий фолбэк No-js

Я вже згадував вище, що ми додамо фолбэк для браузерів з заблокованим JS. Давайте згадаємо, якщо JS відключений, то код не виконується в першу чергу. Тобто ми можемо ефективно використовувати наш клас no-js для лінійного відображення всіх вкладок. Ось класичний приклад на CSS:

/**
* Tabs no-js fallback
*/
.c-tabs.no-js .c-tabs-nav {
display: none;
}
.c-tabs.no-js .c-tab {
display: block;
margin-bottom: 1.5 rem;
}
.c-tabs.no-js .c-tab:last-child {
margin-bottom: 0;
}

Висновок

У цій статті ми розглянули покроковий метод створення адаптивних вкладок. Ефективне використання CSS і JavaScript дозволило нам зв’язати всі разом, а також прикрутити фолбэк для застарілих браузерів. Сподіваюся, вам сподобалася стаття, і вона виявилася корисною. Сподіваюся, вона стане в нагоді вам у ваших майбутніх проектах. Не забувайте про демо і вихідні коди до уроку, які можна подивитися за посиланнями нижче. Якщо у вас виникли питання, залишайте їх у коментарях або пишіть мені.