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

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

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

скачать исходникидемо

Часто вкладки можно встретить в блогах в виде виджетов сайдбаров, в которых, обычно, отображаются последние или любимые посты, или категории. Но помимо второстепенной роли вкладкам (табам) также отводят и главную роль на страницах. К счастью создавать вкладки (табы) для сайта не такое и сложное занятие. В этой статье мы рассмотрим все, что необходимо для их создания, даже позаботимся о устаревших браузерах с выключенным JS. Давайте разберем структуру и разметку вкладок.

Структура и разметка

Итак, наша структура включает в себя:

Секцию меню с вкладками. Активная вкладка подсвечивается

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

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

<div id="tabs" class="c-tabs no-js">
</div>

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

<div id="tabs" class="c-tabs no-js">
  <div class="c-tabs-nav">
    <a href="#" class="c-tabs-nav__link is-active"></a>
    <a href="#" class="c-tabs-nav__link"></a>
    ...
  </div>
</div>

Я сразу сделал первую вкладку активной, первый индекс в JavaScript будет 0. Обратите внимание на то, что количество вкладок может изменяться, но обязательно необходимо, чтобы количество табов и ссылок на них было равным. Перейдем к вкладкам: каждая вкладка это отдельный DIV. У первого блока сразу добавлен класс is-active. Конечная разметка выглядит так:

<div id="tabs" class="c-tabs no-js">
  <div class="c-tabs-nav">
    <a href="#" class="c-tabs-nav__link is-active"></a>
    <a href="#" class="c-tabs-nav__link"></a>
    ...
  </div>
  <div class="c-tab is-active">
    <div class="c-tab__content"></div>
  </div>
  <div class="c-tab">
    <div class="c-tab__content"></div>
  </div>
  ...
</div>

На данном этапе в браузере будет просто куча элементов в одном ряду, но мы это поправим в 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.3s;
}

.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;
}

@media all 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 и span соответственно. На маленьких экранах текст в span скрывается для экономии места. Сделать это можно простым медиа запросом, который мы добавим в конец файла.

Теперь рассмотрим стили для конкретной вкладки:

**
 * Tab
 */
.c-tab {
  display: none;
  background-color: #e7e7e7;
}

.c-tab.is-active {
  display: block;
}

.c-tab__content {
  padding: 1.5rem;
}

Ну тут все просто и интуитивно понятно, но все же пройдемся поэтапно. Каждому табу присвоен класс 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.5rem;
}

.c-tabs.no-js .c-tab:last-child {
  margin-bottom: 0;
}

Заключение

В этой статье мы рассмотрели пошаговый метод создания адаптивных вкладок. Эффективное использование CSS и JavaScript позволило нам связать все вместе, а также прикрутить фолбэк для устаревших браузеров. Надеюсь, вам понравилась статья, и она оказалась полезной. Надеюсь, она пригодится вам в ваших будущих проектах. Не забывайте про демо и исходники к уроку, которые можно посмотреть по ссылкам ниже. Если у вас возникли вопросы, оставляйте их в комментариях или пишите мне.

Источник: http://callmenick.com/

Редакция: Команда webformyself.

Практика HTML5 и CSS3 с нуля до результата!

Получите бесплатный пошаговый видеокурс по основам адаптивной верстки с полного нуля на HTML5 и CSS3

Получить

Метки: ,

Комментарии Вконтакте:

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

Комментарии (3)

  1. black

    отличный вариант, доступно и понятно. Спасибо за пример. Если надо будет Табы расположить вертикально — править HTML & CSS или только CSS?

  2. Георгий

    tabNavigationLinks – коллекция ссылок меню с определенным классом, полученных через option.
    это с каким классом?
    у ссылок класс «c-tabs-nav__link»
    так почему он формирует коллекцию ссылок по классу «tabNavigationLinks»
    querySelectorAll(options.tabNavigationLinks);
    уже голову сломал(

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree