Создать аккордеон, faq, спойлер и подобное, можно при помощи Div и JavaScript.
Но лучше: Details и Summary
Поддерживается всеми современными браузерами и это семантически правильно оформленный код, при использовании которого будут плюсы:
- Людям с ограниченными возможностями проще будет пользоваться вашим сайтом! Их софт (скринридеры и подобное) прекрасно понимает html5 теги и будет правильно обрабатывать их и правильно информировать людей о содержимом.
- Улучшится связанность текста, и поисковики смогут более качественно индексировать сайт, так как будут лучше понимать, как связаны между собой видимый и скрытый текст.
- Будет доступно управление элементами с клавиатуры и других устройств.
- Уменьшается количество javascript кода, который нужно подгружать, что увеличивает скорость загрузки страницы, скорость обработки и корректность.
- Улучшаются показатели в Lighthouse, Google PageSpeed и других подобных инструментах.
- Работает при выключенном javascript.
Минус:
- Старые браузеры не знают таких тегов и не будут скрывать информацию.
HTML:
<details>
<summary>Покажи-скрой меня</summary>
<p>Скандинавская мифология — мифология древних скандинавов</p>
</details>
<details open>
<summary>Покажи-скрой меня 2</summary>
<p>Основным источником сведений о ней являются тексты поэтической </p>
</details>
<details>
<summary>Покажи-скрой меня 3</summary>
<p>Скандинавская мифология — мифология древних скандинавов</p>
</details>
Демонстрация:
С одной стороны выглядит не очень красиво, с другой стороны нейтрально и легко может вписаться во многие дизайны. Кстати, дефолтный вид тега Details очень похож на спойлер от хабра, только нужно чуть перекрасить, сделать подчеркивание и получим семантически правильный, без javascript и дивов, хабровский спойлер.
К сожалению, у дефолтного маркера есть два недостатка:
- Старые браузеры его не видят.
- Вебкит баузеры не позволяют менять символ маркера.
По этой причине, дефолтный маркер надо спрятать и создать свой.
Рассмотрим первый пример Details/Summary с измененным текстовым маркером:
CSS:
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:inline-block;
padding: .3em .5em .3em .4em;
font-size:1.4em;
cursor: pointer;
}
summary:before {
content: "+";
margin-right: .3em;
}
details[open] > summary:before {
content: "–";
}
summary ~ * {
padding:0 1em 0 1em;
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
details{
display:block;
margin-bottom: .5rem;
}
Дефолтным маркерам делаем display:none и показываем альтернативный при помощи summary:before {content: "+";}
summary:focus — обводка при помощи box-shadow, это нужно для клавиатуры, чтоб видно было активный элемент и можно было перемещаться клавишей таб и открывать и закрывать при помощи пробела.
Для тега summary я поставил display:inline-block — это чтоб он не растягивался на всю ширину и были кликабельными только слова, а не вся строка.
Демонстрация:
Текстовый маркер справа + простейшая анимация текста и маркера:
CSS:
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:inline-block;
padding: .3em .5em .3em .4em;
font-size:1.4em;
cursor: pointer;
}
summary:after {
content: "+";
margin-left: .3em;
display: inline-block;
transition: transform .5s;
}
details[open] > summary:after {
transform: scale(1,-1);
}
summary ~ * {
padding:0 1em 0 1em;
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
details[open] summary ~ *{
animation: sweep .5s ease-in-out;
}
@keyframes sweep {
0% {opacity: 0;}
100% {opacity: 1;}
}
details{
display:block;
margin-bottom: .5rem;
}
В новом примере я использую для маркера summary:after вместо summary:before, для того чтоб он отображался справа.
Анимация маркера при помощи transform: scale(1,-1);
Всем элементам, которые находится после summary, ставлю анимацию плавного появления при помощи animation: sweep .5s ease-in-out;
Демонстрация:
Svg маркер + анимация поворота:
CSS:
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:inline-block;
padding: .3em .6em .3em 1.5em;
font-size:1.4em;
cursor: pointer;
position: relative;
}
summary:before {
left: .3em;
top: .4em;
color: transparent;
background: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjM0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIzNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOC41OSAxNi4zNGw0LjU4LTQuNTktNC41OC00LjU5TDEwIDUuNzVsNiA2LTYgNnoiLz48L3N2Zz4=") no-repeat 50% 50% / 1em 1em;
width: 1em;
height: 1em;
content: "";
position: absolute;
transition: transform .5s;
}
details[open] > summary:before {
transform: rotateZ(90deg);
}
summary ~ * {
padding:0 1em 0 1em;
}
details[open] summary ~ *{
animation: sweep .5s ease-in-out;
}
@keyframes sweep {
0% {opacity: 0;}
100% {opacity: 1;}
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
details{
display:block;
margin-bottom: .5rem;
}
Summary:before пришлось серьезно переделать:
- Поставить position: absolute; left: .3em; top: .4em; width: 1em; height: 1em;
- Текстовому маркеру надо обязательно поставить color: transparent; иначе он будет виден.
- Картинку вешаем при помощи background.
Так же нужно у summary поставить отступ padding-left: 1.5em, чтоб текст и иконка не накладывались друг на друга.
Ну и добавляем transform: rotateZ(90deg) для красивого поворота стрелки.
Демонстрация:
Если нам нужна svg иконка справа, то нужно поменять summary:before и вместо left поставить right.
Для summary поставить padding-right: 1.5em;
CSS:
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:inline-block;
padding: .3em 1.5em .3em .6em;
font-size:1.4em;
cursor: pointer;
position: relative;
}
summary:before {
right: .3em;
top: .4em;
color: transparent;
background: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjM0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIzNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOC41OSAxNi4zNGw0LjU4LTQuNTktNC41OC00LjU5TDEwIDUuNzVsNiA2LTYgNnoiLz48L3N2Zz4=") no-repeat 50% 50% / 1em 1em;
width: 1em;
height: 1em;
content: "";
position: absolute;
transition: transform .5s;
}
details[open] > summary:before {
transform: rotateZ(90deg);
}
summary ~ * {
padding:0 1em 0 1em;
}
details[open] summary ~ *{
animation: sweep .5s ease-in-out;
}
@keyframes sweep {
0% {opacity: 0;}
100% {opacity: 1;}
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
details{
display:block;
margin-bottom: .5rem;
}
Демонстрация:
Давайте теперь сделаем один из наиболее распространенных примеров создания аккордиона, где будет иконка слева, фон, тени, эффекты:
CSS:
body{background: #edf2f7;}
details{
display:block;
background: #fff;
width:400px;
box-shadow: 0 10px 15px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
border-radius: 8px;
overflow:hidden;
margin-bottom: 1.5rem;
}
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:block;
padding: .3em .3em .3em 1.4em;
font-size:1.4em;
cursor: pointer;
position: relative;
border-bottom: 1px solid #e2e8f0;
}
summary:before {
top: .4em;
left: .3em;
color: transparent;
background: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjM0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIzNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOC41OSAxNi4zNGw0LjU4LTQuNTktNC41OC00LjU5TDEwIDUuNzVsNiA2LTYgNnoiLz48L3N2Zz4=") no-repeat 50% 50% / 1em 1em;
width: 1em;
height: 1em;
content: "";
position: absolute;
transition: transform .5s;
}
details[open] > summary:before {
transform: rotateZ(90deg);
}
summary ~ * {
padding: 0 2em 10px 2em;
}
details[open] summary ~ *{
animation: sweep .5s ease-in-out;
}
@keyframes sweep {
0% {opacity: 0;}
100% {opacity: 1;}
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
Демонстрация:
Svg маркер справа + эффект зеркального поворота стрелки:
CSS:
body{background: #edf2f7;}
details{
display:block;
background: #fff;
width:400px;
box-shadow: 0 10px 15px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
border-radius: 8px;
overflow:hidden;
margin-bottom: 1.5rem;
}
summary::-webkit-details-marker{display:none;}
summary::-moz-list-bullet{list-style-type:none;}
summary::marker{display:none;}
summary {
display:block;
padding: .3em 1em .3em .9em;
border-bottom: 1px solid #e2e8f0;
font-size:1.4em;
cursor: pointer;
position: relative;
}
summary:before {
top: .4em;
right: .3em;
color: transparent;
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTYuNTkgOC41OUwxMiAxMy4xNyA3LjQxIDguNTkgNiAxMGw2IDYgNi02eiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48L3N2Zz4=") no-repeat 50% 50% / 1em 1em;
width: 1em;
height: 1em;
content: "";
position: absolute;
transition: transform .5s;
}
details[open] > summary:before {
transform: scale(1,-1);
}
summary ~ * {
padding: 0 1em 10px 1.4em;
}
details[open] summary ~ *{
animation: sweep .5s ease-in-out;
}
@keyframes sweep {
0% {opacity: 0;}
100% {opacity: 1;}
}
summary:focus {
outline:0;
box-shadow: inset 0 0 1px rgba(0,0,0,0.3), inset 0 0 2px rgba(0,0,0,0.3);
}
Демонстрация:
Теперь вы можете создавать красивые аккордионы, спойлеры и faq, без JavaScript, на чистом HTML5 и CSS.
Прежде чем убирать outline, 100 раз подумайте, чем вы можете его заменить, чтоб человек мог видеть фокус и мог перемещаться с клавиатуры или других устройств.
Если вам нужно, чтоб при открытии одного спойлера, закрывались остальные, то придется применить javascript, ниже привожу пример простого JS кода, который решит эту проблему.
JavaScript:
var details = document.querySelectorAll("details");
for(i=0;i<details.length;i++) {
details[i].addEventListener("toggle", accordion);
}
function accordion(event) {
if (!event.target.open) return;
var details = event.target.parentNode.children;
for(i=0;i<details.length;i++) {
if (details[i].tagName != "DETAILS" ||
!details[i].hasAttribute('open') ||
event.target == details[i]) {
continue;
}
details[i].removeAttribute("open");
}
}
Демонстрация:
Таблица поддержки Details/Summary браузерами.
С уважением, создатель конструктора лэндингов для фрилансеров CMS cPortfolio
Комментарии (13)
ivan386
02.09.2019 10:28Для старых браузеров можно собрать аналог details на css используя checkbox, label, div и селектор :checked.
Аналогично если не хочется Javascript использовать для автозакрывающихся details можно собрать аналог на radio, label, div и том же селекторе :checked.
iefedorov Автор
02.09.2019 10:36Да, вы правы, ваш вариант — это что-то среднее между details и между кодом на JavaScript! И в ряде случаев :checked это идеальный вариант!)
Но семантика при этом опускается…
namikiri
02.09.2019 13:13border-radius: 8px;
Вот с этого-то момента Вы всё и испоганили.
В остальном спасибо за статью, открыл для себя новые возможности HTML5.
keydon2
Круто. В идеале хочу совсем уйти от js на простеньких сайтах.