. . .

Tab-панель

19 Июнь 2019

Tab-панель или панель вкладок, это блок сайта, представляющий собой блок текста и вкладки для переключения этого текста. Это довольно простой и распространенный элемент сайта, он позволяет на небольшом пространстве расположить значительное количество информации.

Я опишу два подхода к решению этой задачи. Первый подход был изучен мной, когда я только начинал изучать практическую часть javascript. Второй - это мое решение стоящей задачи, так как ее решил бы лично я.

Html и css в обоих случаях будут совпадать, разница будет только в js-коде, по этому разметку и стили я опишу один раз.

Начнем с создания html-разметки:

<div id="tabs">
    <div class="tab tab-active">Вкладка 1</div>
    <div class="tab">Вкладка 2</div>
    <div class="tab">Вкладка 3</div>
    <div class="tab-content content-active">Текст вкладки 1</div>
    <div class="tab-content">Текст вкладки 2</div>
    <div class="tab-content">Текст вкладки 3</div>
</div>

Теперь опишем стили:

#tabs {
    display: block;
    position: relative;
    width: 50%;
    margin-top: 30px;
}
#tabs .tab {
    display: inline;
    padding: 10px 15px;
    cursor: pointer;
}
#tabs .tab-active {
    background-color: #ddd;
}
#tabs .tab-content {
    width: 100%;
    padding: 20px 15px;
    box-sizing: border-box;
    margin-top: 9px;
    display: none;
}
#tabs .content-active {
    background-color: #ddd;
    display: block;
}

На этом этапе tab-панель выглядит следующим образом:

Вкладка 1
Вкладка 2
Вкладка 3
Текст вкладки 1
Текст вкладки 2
Текст вкладки 3

Теперь мы напишем js код согласно первому методу:

//собираем все кнопки вкладок
var tabs = document.getElementsByClassName('tab');
//собираем весь текст
var tContent = document.getElementsByClassName('tab-content');
//создаем функцию, которая убирает класс активности
//функция принимает аргумент а
//начиная с этого аргумента и до конца объекта она стирает класс активности
function hideTabContent (a) {
    for (var i=a; i<tContent.length; i++) {
        tabs[i].classList.remove('tab-active');
        tContent[i].classList.remove('content-active');
    }
}
        
//создаем функцию, которая добавялет класс активности
//функция принимает аргумент b - тот элемент, который нужно сделать активным
function showTabContent (b) {
    //проверка является ли этот элемент уже активным
    if(!tabs[b].classList.contains('tab-active')) {
        //стираем у всех элементов класс активности
        hideTabContent(0);
        //добавляем нужному элементу класс активности
        tabs[b].classList += ' tab-active';
        tContent[b].classList.add('content-active');
    }
}
//создаем функцию, которая отслеживает все клики в блоке табов
document.getElementById('tabs').onclick = function (event) {
    //создаем переменную, в которую записывается объект клика
    var target = event.target;
    //если объектом клика является кнопка вкладки
    if (target.className == 'tab') {
        //перебираем циклом все кнопки
        for (var i=0; i<tabs.length; i++) {
            //если кнопка из цикла является объектом клика
            if (target == tabs[i]) {
                //выполняем функцию показа с аргументом, соответствующим нажатой кнопке
                showTabContent(i);
                //прерываем выполнение функции
                break;
            }
        }
    }
}
Вкладка 1
Вкладка 2
Вкладка 3
Текст вкладки 1
Текст вкладки 2
Текст вкладки 3

Результат, как вы видите, работает. Можно приступить к описанию второго метода - так как вижу это я. Мой метод использует конструкцию немедленно выполняемой функции - Immediately-Invoked Function Expression.

//собираем все кнопки
var tabs = document.getElementsByClassName('tab');
//собираем все тексты
var tContent = document.getElementsByClassName('tab-content');
//запускаем цикл for, который проходит по всем кнопкам
for (var i=0; i<tabs.length; i++) {
    //назначаем функцию по клику на каждой кнопке
    //на кнопку назначаем немедленно выполняемую функцию
    //функция имеет аргумент а в который мы передадим значение i
    tabs[i].onclick = (function(a) {
        return function () {
            //если кнопка не является уже активной
            if(!tabs[a].classList.contains('tab-active')) {
                //делаем кнопку активной
                tabs[a].classList += ' tab-active';
                //делаем текст активным
                tContent[a].classList += ' content-active';
                //проходим циклом и удаляем активность до нажатой кнопки
                for (var x=0; x<a; x++) {
                    tabs[x].classList.remove('tab-active');
                    tContent[x].classList.remove('content-active');
                }
                //проходим циклом и удаляем активность после нажатой кнопки
                for (var y=a+1; y<tabs.length; y++) {
                    tabs[y].classList.remove('tab-active');
                    tContent[y].classList.remove('content-active');
                }
            }
        }
    })(i)
}

Как видно, этот подход тоже работает:

Вкладка 1
Вкладка 2
Вкладка 3
Текст вкладки 1
Текст вкладки 2
Текст вкладки 3

Какой подход из предложенных удобнее - дело персональное. Мой подход кажется мне более удобным в основном из-за того, что его написал я. Но мне кажется более удобным то, что он по сути работает в рамках одной функции, и проще читается.

Посмотреть на github