Часто бывает, что нужно сделать раскрывающийся “аккордеон” список. Раньше для это требовалось писать 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
})
})