Аккордеон меню на jQuery

Напишем простое и универсальное аккордеон меню на jQuery

extension
Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi, minima iste libero impedit natus maxime fugit totam! Expedita, rerum necessitatibus sint adipisci architecto harum, sequi perferendis voluptatum temporibus ipsa nam dignissimos iure minus.
eco
Dolor sit amet.
Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi, minima iste libero impedit natus maxime fugit totam! Expedita, rerum necessitatibus sint adipisci architecto harum, sequi perferendis voluptatum temporibus ipsa nam dignissimos iure minus.
fingerprint
Rerum necessitatibus
Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi

Этот пример на Codepen

Создаём HTML основу

Пункты меню располагаются в родительском блоке <div class="about__items about-items"></div>

Каждый пункт меню <div class="about__item about-item"></div> имеет заголовок <div class="about-item__head"></div> и область контента <div class="about-item__body"></div>

<section class="about">
  <div class="about__items about-items"> <!-- родительский блок -->
    <div class="about__item about-item"> <!-- блок пункта меню -->
      <div class="about-item__head"> <!-- блок заголовка -->
        <div class="about-item__icon"><span class="material-icons">extension</span></div>
        <div class="about-item__title">Lorem ipsum dolor sit amet.</div>
      </div>
      <div class="about-item__body">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi, minima iste libero impedit natus maxime fugit totam! Expedita, rerum necessitatibus sint adipisci architecto harum, sequi perferendis voluptatum temporibus ipsa nam dignissimos iure minus.</div> <!-- блок контента -->
    </div>
    <div class="about__item about-item">
      <div class="about-item__head">
        <div class="about-item__icon"><span class="material-icons">eco</span></div>
        <div class="about-item__title">Dolor sit amet.</div>
      </div>
      <div class="about-item__body">Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi, minima iste libero impedit natus maxime fugit totam! Expedita, rerum necessitatibus sint adipisci architecto harum, sequi perferendis voluptatum temporibus ipsa nam dignissimos iure minus.</div>
    </div>
    <div class="about__item about-item">
      <div class="about-item__head">
        <div class="about-item__icon"><span class="material-icons">fingerprint</span></div>
        <div class="about-item__title">Rerum necessitatibus</div>
      </div>
      <div class="about-item__body">Consectetur blanditiis doloribus odio, voluptas vero perferendis assumenda quaerat rem quibusdam repudiandae eaque sed sapiente officia expedita eligendi</div>
    </div>
  </div>
</section>


Опишем стили CSS

При добавлении активного класса .about-item_active блоку .about-item зададим стили для его элементов .about-item__head и .about-item__icon

.about {
  padding: 60px 0;
}
.about__item:not(:last-child) {
  margin-bottom: 15px;
}
.about-item__head {
  background: #283845;
  padding: 15px 30px;
  color: #fff;
  font-size: 18px;
  border-radius: 8px;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none; /* отключаем выделение текста при нажатии на блок заголовка */
  display: flex;
  align-items: center;
  transition: 0.3s ease;
}
.about-item__head:hover {
  background: #24323e;
}
.about-item__title {
  font-weight: 700;
}
.about-item__icon {
  margin-right: 15px;
  line-height: 1;
  transition: 0.3s ease;
}
.about-item__body {
  padding: 30px;
  font-size: 16px;
  background: #fff;
  border-radius: 8px;
  color: #283845;
  display: none;
  margin-top: 10px;
}
.about-item_active .about-item__head {
  background: #22303b; /* в активном состоянии блока .about__item фон заголовка .about-item__head меняет цвет */
}
.about-item_active .about-item__icon {
  color: #8cb369; /* в активном состоянии блока .about__item иконка .about-item__icon меняет цвет */
}


Пишем jQuery код

jQuery код с комментариями

$(document).ready(function(){ // DOM готов для взаимодействия с ним

  function accordion(parentSelector, itemSelector, itemActiveClass, headSelector, contentSelector, duration) { // объявляем функцию и принимаем параметры

    let locked = false // объявляем переменную для блокировки клика при активной анимации

    $(parentSelector).click(function(e){ // при клике на родительский блок, селектор которого передаем первым параметром (parentSelector)

      const target = $(e.target) // записываем в константу элемент, где был клик
      const item = target.closest(itemSelector) // ищем ближайший родительский блок, где был клик, по селектору, который передаем вторым параметром (itemSelector)
      const itemHead = item.find(headSelector) // ищем заголовок (4й параметр) в блоке, где был клик
      const itemContent = item.find(contentSelector) // ищем блок контента в блоке (5й параметр), где был клик

      const siblings = item.siblings() // ищем соседние блоки, расположенные на том же уровне, что и блок, где был клик
      const siblingsContent = siblings.find(contentSelector) // ищем блок контента(5й параметр) у соседских блоков

      if (!target.closest(headSelector).length) return // если клик был не на блоке заголовка, прерываем функцию, то есть ничего не делаем

      if (locked) return // если клик заблокирован, тоже прерываем функцию

      if (!item.hasClass(itemActiveClass)) { // если блок, где был клик, не имеет активного класса
        locked = true // блокируем клик
        siblings.removeClass(itemActiveClass) // удаляем у соседских блоков активный класс
        siblingsContent.slideUp(duration) // сворачиваем блок контента у соседских блоков (6 параметр - duration - продолжительсность анимации)
        item.addClass(itemActiveClass) // добавляем активный класс блоку, где был клик
        itemContent.slideDown(duration, function(){ // разворачиваем блок контента у блока, где был клик, после окончания анимации запускаем другую функцию, чтобы снять блокировку клика
          locked = false
        })
      } else { // если блок, где был клик, имеет активный класс
        locked = true // блокируем клик
        item.removeClass(itemActiveClass) // удаляем активный класс блоку, где был клик
        itemContent.slideUp(duration, function(){ // сворачиваем блок контента у блока, где был клик, после окончания анимации запускаем другую функцию, чтобы снять блокировку клика
          locked = false
        })
      }
    })
  }

  accordion('.about-items', '.about-item', 'about-item_active', '.about-item__head', '.about-item__body', 300) // запускаем функцию с параметрами

  accordion('.briefly-cards', '.briefly-card', 'briefly-card_active', '.briefly-card__head', '.briefly-card__text', 600) // если будут еще похожие меню, но с другими названиями классов, то запускаем эту же самую функцию, передавая ей просто другие параметры

})


Передаваемые параметры
  1. parentSelector - селектор родительского блока - '.about-items'
  2. itemSelector - селектор блока пункта меню - '.about-item'
  3. itemActiveClass - активный класс блока пункта меню - 'about-item_active'
  4. headSelector - селектор блока заголовка - '.about-item__head'
  5. contentSelector - селектор блока контента - '.about-item__body'
  6. duration - продолжительность анимации разворачивания/сворачивания блока контента - 300


Описание некоторых нюансов
  1. Блокировка клика нужна для того, чтобы при многократном клике на заголовок анимация не запускалась несколько раз. Как только анимация запускается, блокируем клик, как только анимация заканчивается, снимаем блокировку
  2. Параметры передаем в том порядке в котором они записаны. В данном случае параметры передаем строками в кавычках. Если это селектор, не забываем ставить точку ('.about-item'). Если это название класса, указываем без точки ('about-item_active')
  3. Последним параметром передаем число без кавычек - продолжительность анимации разворачивания/сворачивания блока контента


Полезные ссылки

Обработчик готовности дерева DOM - .ready()

Объект event - function(e){}

Переменные и константы - let и const

Ближайший подходящий предок - .closest()

Поиск элементов внутри выбранных - .find()

Поиск элементов лежащих на одном уровне - .siblings()

Добавление класса - .addClass()

Удаление класса - .removeClass()

Разворачивание и сворачивание элементов - .slideDown() .slideUp()