Аккордеон на HTML и CSS

Делаем раскрывающийся аккордеон список без использования javaScript

Часто бывает, что нужно сделать раскрывающийся “аккордеон” список. Раньше для это требовалось писать JavaScript. Теперь в HTML5 появился тег details, который предоставляет возможность делать раскрывающийся блок без написания JS.

Весь контент оборачивается в тег details, а заголовок при нажатии на который будет раскрываться контент пишется в тег summary. Сам же контент идет после заголовка в любом удобном для вас виде.

<details>
    <summary>Заголовок</summary>
    <p>Раскрывающийся контент</p>
</details>
Заголовок

Раскрывающийся контент

Дефолтный дизайн выглядит очень просто, но и изменяется он без проблем. Стилизуется все как обычно, только для скрытия стандартной стрелки используется свойством list-style.

summary {
    list-style: none;
}
Заголовок

Раскрывающийся контент

Если необходимо чтобы блок был изначально раскрытым, то в этом случает тегу details добавляется атрибут open.

<details open>
    <summary>Заголовок</summary>
    <p>Раскрывающийся контент</p>
</details>
Заголовок

Раскрывающийся контент

Анимация

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

Но с анимацией не все так просто. Тут есть свои нюансы и несколько способов анимации.

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

Второй способ, это анимировать, блок с контентом внутри, например делать ему отрицательный margin и менять его в 0 при открытии блока, имитирую изменение высоты. Но работает этот способ не стабильно, из-за специфики работы тега details. Анимация работает только на открытие и не всегда, может сработать а может и нет.

Третий способ это открытому тегу details добавить padding снизу и тегу summary margin-bottom. Это наверное лучшее решение, которое работает в обе стороны и выглядит более-менее прилично. Более хорошую плавность анимации можно достичь играясь с размерами padding, еще как вариант на этот padding, можно сместить контент отрицательным margin.

Закрытие блока

А что если необходимо при открытии одного списка скрывать все остальные? Тут к сожалению уже не обойтись без JavaScript.

// Получаем все элементы details
const details = document.querySelectorAll('details');

// Проходимся по каждому элементу
details.forEach(item => {

  // На каждый элемент вешаем слушатель события клика
  item.addEventListener('click', (e) => {

    // Сбрасываем стандартное действие при клике
    e.preventDefault();
    
    // Находим открытый элемент
    const openItem = document.querySelector('details[open]');
    
    // Если есть открытый элемент удаляем ему атрибут open
    if (openItem) openItem.open = false;

    // Если открытый элемент не является тем, на который мы нажали, то нажатому элементу добавляем атрибут open
    if (openItem !== item) item.open = true 
  })
})