Аккордеон, faq, спойлер и другие раскрывающиеся виджеты

в 4:05, , рубрики: css, details, html, html5, javascript, Разработка веб-сайтов

Аккордеон и faq

Создать аккордеон, faq, спойлер и подобное, можно при помощи Div и JavaScript.
Но лучше: Details и Summary

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

  1. Людям с ограниченными возможностями проще будет пользоваться вашим сайтом! Их софт (скринридеры и подобное) прекрасно понимает html5 теги и будет правильно обрабатывать их и правильно информировать людей о содержимом.
  2. Улучшится связанность текста, и поисковики смогут более качественно индексировать сайт, так как будут лучше понимать, как связаны между собой видимый и скрытый текст.
  3. Будет доступно управление элементами с клавиатуры и других устройств.
  4. Уменьшается количество javascript кода, который нужно подгружать, что увеличивает скорость загрузки страницы, скорость обработки и корректность.
  5. Улучшаются показатели в Lighthouse, Google PageSpeed и других подобных инструментах.
  6. Работает при выключенном javascript.


Минус:

  1. Старые браузеры не знают таких тегов и не будут скрывать информацию.

HTML:

<details>
    <summary>Покажи-скрой меня</summary>
    <p>Скандинавская мифология — мифология древних скандинавов</p>
</details>
<details open>
    <summary>Покажи-скрой меня 2</summary>
    <p>Основным источником сведений о ней являются тексты поэтической </p>
</details>
<details>
    <summary>Покажи-скрой меня 3</summary>
    <p>Скандинавская мифология — мифология древних скандинавов</p>
</details>

Простой пример Details/Summary

Демонстрация:

С одной стороны выглядит не очень красиво, с другой стороны нейтрально и легко может вписаться во многие дизайны. Кстати, дефолтный вид тега Details очень похож на спойлер от хабра, только нужно чуть перекрасить, сделать подчеркивание и получим семантически правильный, без javascript и дивов, хабровский спойлер.

Спойлер от хабра

К сожалению, у дефолтного маркера есть два недостатка:

  1. Старые браузеры его не видят.
  2. Вебкит баузеры не позволяют менять символ маркера.

По этой причине, дефолтный маркер надо спрятать и создать свой.

Рассмотрим первый пример Details/Summary с измененным текстовым маркером:

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 — это чтоб он не растягивался на всю ширину и были кликабельными только слова, а не вся строка.

Демонстрация:

Текстовый маркер справа + простейшая анимация текста и маркера:

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: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 маркер + анимация поворота:

Details/Summary - 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 пришлось серьезно переделать:

  1. Поставить position: absolute; left: .3em; top: .4em; width: 1em; height: 1em;
  2. Текстовому маркеру надо обязательно поставить color: transparent; иначе он будет виден.
  3. Картинку вешаем при помощи background.

Так же нужно у summary поставить отступ padding-left: 1.5em, чтоб текст и иконка не накладывались друг на друга.

Ну и добавляем transform: rotateZ(90deg) для красивого поворота стрелки.

Демонстрация:

Если нам нужна svg иконка справа, то нужно поменять summary:before и вместо left поставить right.
Для summary поставить padding-right: 1.5em;

Details/Summary - 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 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 .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);
}

Демонстрация:

Теперь вы можете создавать красивые аккордионы, спойлеры и 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 браузерами.

С вами был разработчик конструктора лэндингов cPortfolio!

Автор: Игорь

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js