. . .

Аккордеон

18 Июнь 2019

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

Он так называет, потому визуально принцип его работы похож на работу одноименного инструмента. При растягивании инструмента расширяются его секции.

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

<div id="accordion">
    <div class="acc-item">
        <button class="acc-btn">Кнопка 1</button>
        <p class="acc-text">Текст первого блока</p>
    </div>
    <div class="acc-item">
        <button class="acc-btn">Кнопка 2</button>
        <p class="acc-text">Текст второго блока</p>
    </div>
    <div class="acc-item">
        <button class="acc-btn">Кнопка 3</button>
        <p class="acc-text">Текст третьего блока</p>
    </div>
</div>

Его стили-css:

#accordion {
    width: 50%;
    position: relative;
    margin-top: 20px;
    text-align: center;
}
.acc-item {
    border: 1px solid #aaa;
}
.acc-item .acc-btn {
    display: block;
    width: 100%;
    padding: 15px 0;
    border: none;
}
.acc-item .acc-text {
    padding: 20px 0;
    display: none;
}
.acc-item .acc-text-active {
    display: block;
}

На данном этапе блок выглядит так:

Текст первого блока

Текст второго блока

Текст третьего блока

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

//создаем переменную accBtn, которая собирает объект из всех кнопок
var accBtn = document.getElementsByClassName('acc-btn');
//создаем переменную accText, которая собирает объект из всех текстов
var accText = document.getElementsByClassName('acc-text');
//перебираем циклом for все кнопки
for (var i=0; i<accBtn.length; i++) {
    //на каждую кнопку кидаем функцию по клику
    accBtn[i].onclick = function () {
        //добавляем класс текстовому элементу, соответствующему кнопке
        accText[i].classList.toggle('acc-text-active');
    }
}

Однако так код будет выдавать ошибку, поскольку внутри функции i уже будет равен в нашем случае тройке. Для того чтобы в памяти функции сохранялся i кнопки, нам поможет конструкция Immediately-Invoked Function Expression, или немедленно вызываемой функции. Она выглядит следующим образом:

(function () { наш код })()

В первых скобках содержится функциональное выражение, а во вторых - передаваемые аргументы. Перепишем наш код:

for (var i=0; i<accBtn.length; i++) {
    //здесь a будет аргументом, который должна принять функция
    accBtn[i].onclick = (function (a) {
        return function () {
            accText[a].classList.toggle('acc-text-active');
        }
    //здесь i - аргумент, который мы передаем в а
    })(i)
}

Теперь посмотрим на рабочий пример этого кода:

Текст первого блока

Текст второго блока

Текст третьего блока

По большому счету аккордеон готов. Но хотелось бы довести его до совершенства, а именно сделать так, чтобы при раскрытии одного блока, все остальные открытые закрывались.

Для этого необходимо добавить в функцию два цикла for:

var accBtn = document.getElementsByClassName('acc-btn1');
var accText = document.getElementsByClassName('acc-text1');
for (var i=0; i<accItem.length; i++) {
    accBtn[i].onclick = (function(a) {
        return function () {
            accText[a].classList.toggle('acc-text-active');
            //перебираем все accText до а
            for (var x=0; x<a; x++) {
                //удаляем у них класс acc-text-active
                accText[x].classList.remove('acc-text-active');
            }
            //перебираем все accText после а
            for (var y=a+1; y<accText.length; y++) {
                //удаляем у них класс acc-text-active
                accText[y].classList.remove('acc-text-active');
            }
        }
    })(i)
}

Рабочий результат:

Текст первого блока

Текст второго блока

Текст третьего блока

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