При создании accrodion на JS работает только первая строка

Создаю accrodion на чистом JavaScript с анимацией. Проблема в том, что только первая строка работает как мне надо, остальные не открываются:

let slideUp = (target, duration = 500) => {
  target.style.transitionProperty = 'height, margin, padding';
  target.style.transitionDuration = duration + 'ms';
  target.style.boxSizing = 'border-box';
  target.style.height = target.offsetHeight + 'px';
  target.offsetHeight;
  target.style.overflow = 'hidden';
  target.style.height = 0;
  target.style.paddingTop = 0;
  target.style.paddingBottom = 0;
  target.style.marginTop = 0;
  target.style.marginBottom = 0;

  window.setTimeout(() => {
    target.style.display = 'none';
    target.style.removeProperty('height');
    target.style.removeProperty('padding-top');
    target.style.removeProperty('padding-bottom');
    target.style.removeProperty('margin-top');
    target.style.removeProperty('margin-bottom');
    target.style.removeProperty('overflow');
    target.style.removeProperty('transition-duration');
    target.style.removeProperty('transition-property');
  }, duration);
}

let slideDown = (target, duration = 500) => {
  target.style.removeProperty('display');
  let display = window.getComputedStyle(target).display;

  if (display === 'none')
    display = 'block';

  target.style.display = display;
  let height = target.offsetHeight;
  target.style.overflow = 'hidden';
  target.style.height = 0;
  target.style.paddingTop = 0;
  target.style.paddingBottom = 0;
  target.style.marginTop = 0;
  target.style.marginBottom = 0;
  target.offsetHeight;
  target.style.boxSizing = 'border-box';
  target.style.transitionProperty = "height, margin, padding";
  target.style.transitionDuration = duration + 'ms';
  target.style.height = height + 'px';
  target.style.removeProperty('padding-top');
  target.style.removeProperty('padding-bottom');
  target.style.removeProperty('margin-top');
  target.style.removeProperty('margin-bottom');
  window.setTimeout(() => {
    target.style.removeProperty('height');
    target.style.removeProperty('overflow');
    target.style.removeProperty('transition-duration');
    target.style.removeProperty('transition-property');
  }, duration);
}

let slideToggle = (target, duration = 500) => {
  if (window.getComputedStyle(target).display === 'none') {
    return slideDown(target, duration);
  } else {
    return slideUp(target, duration);
  }
}
var accordionTitle = document.querySelector('.accordion-title');

accordionTitle.addEventListener('click', function () {
slideToggle(document.querySelector('.collapse'), 400);
accordionTitle.classList.toggle('collapsed');
});
.collapse:not(.show) {
  display: none;
}
<div class="accordion-card">
  <button class="accordion-title collapsed">
        Значения параметров
    </button>
  <div class="collapse">
    <p>
      Аргумент должен соответствовать допустимой строке селектора, содержащей один или несколько селекторов. При указании нескольких селекторов необходимо разделять значения запятыми. Если по какой-то причине вы используете в наименовании селекторов символы,
      которые не являются частью стандартного синтаксиса CSS, то при поиске такие символы должны быть экранированы с помощью символа обратной косой черты ("\"). Поскольку обратная косая черта также является специальным символом (escape) в JavaScript,
      то при вводе литеральной строки ее необходимо экранировать дважды. Обязательный параметр.
    </p>
  </div>
</div>

<div class="accordion-card">
  <button class="accordion-title collapsed">
        Исключения
    </button>

  <div class="collapse">
    <p>
      Возникает в том случае, если синтаксис указанного селектора или селекторов некорректен.
    </p>
  </div>
</div>

<div class="accordion-card">
  <button class="accordion-title collapsed">
        Результат нашего примера:
    </button>

  <div class="collapse">
    <p>
      В этом примере с использованием атрибута событий onclick при нажатии на кнопку (HTML элемент) вызываем функцию myFunc(), которая с использованием JavaScript метода document.querySelectorAll() выбирает все элементы с классом block и инициализируем переменную
      этим значением (объект NodeList). После этого мы проходим циклом по всем элементам этого массивоподобного объекта (объект NodeList) и устанавливаем зеленый цвет текста каждому элементу.
    </p>
  </div>
</div>

Уверен проблема в этом куске:

accordionTitle.addEventListener('click', function () {
    slideToggle(document.querySelector('.collapse'), 400);
    accordionTitle.classList.toggle('collapsed');
});

Ответы (1 шт):

Автор решения: DemoS

Строкой var accordionTitle = document.querySelector('.accordion-title'); вы берете как раз только первый элемент и вешаете на него прослушку клика, потому так и происходит. Вам нужно вешать на все, то есть и брать все посредством document.querySelectorAll. А в обработчике что-то вроде брать следующий элемент (который собственно и нужно свернуть/развернуть) через https://developer.mozilla.org/ru/docs/Web/API/Node/nextSibling

В общем немного подправил ваш код

let slideUp = (target, duration = 500) => {
  target.style.transitionProperty = 'height, margin, padding';
  target.style.transitionDuration = duration + 'ms';
  target.style.boxSizing = 'border-box';
  target.style.height = target.offsetHeight + 'px';
  target.offsetHeight;
  target.style.overflow = 'hidden';
  target.style.height = 0;
  target.style.paddingTop = 0;
  target.style.paddingBottom = 0;
  target.style.marginTop = 0;
  target.style.marginBottom = 0;

  window.setTimeout(() => {
    target.style.display = 'none';
    target.style.removeProperty('height');
    target.style.removeProperty('padding-top');
    target.style.removeProperty('padding-bottom');
    target.style.removeProperty('margin-top');
    target.style.removeProperty('margin-bottom');
    target.style.removeProperty('overflow');
    target.style.removeProperty('transition-duration');
    target.style.removeProperty('transition-property');
  }, duration);
}

let slideDown = (target, duration = 500) => {
  target.style.removeProperty('display');
  let display = window.getComputedStyle(target).display;

  if (display === 'none')
    display = 'block';

  target.style.display = display;
  let height = target.offsetHeight;
  target.style.overflow = 'hidden';
  target.style.height = 0;
  target.style.paddingTop = 0;
  target.style.paddingBottom = 0;
  target.style.marginTop = 0;
  target.style.marginBottom = 0;
  target.offsetHeight;
  target.style.boxSizing = 'border-box';
  target.style.transitionProperty = "height, margin, padding";
  target.style.transitionDuration = duration + 'ms';
  target.style.height = height + 'px';
  target.style.removeProperty('padding-top');
  target.style.removeProperty('padding-bottom');
  target.style.removeProperty('margin-top');
  target.style.removeProperty('margin-bottom');
  window.setTimeout(() => {
    target.style.removeProperty('height');
    target.style.removeProperty('overflow');
    target.style.removeProperty('transition-duration');
    target.style.removeProperty('transition-property');
  }, duration);
}

let slideToggle = (target, duration = 500) => {
  if (window.getComputedStyle(target).display === 'none') {
    return slideDown(target, duration);
  } else {
    return slideUp(target, duration);
  }
}
var accordionTitles = document.querySelectorAll('.accordion-title');

accordionTitles.forEach( e => {e.addEventListener('click', function (s) {
slideToggle(s.target.nextSibling.nextElementSibling, 400);
s.target.classList.toggle('collapsed');
})});
.collapse:not(.show) {
  display: none;
}
<div class="accordion-card">
  <button class="accordion-title collapsed">
        Значения параметров
    </button>
  <div class="collapse">
    <p>
      Аргумент должен соответствовать допустимой строке селектора, содержащей один или несколько селекторов. При указании нескольких селекторов необходимо разделять значения запятыми. Если по какой-то причине вы используете в наименовании селекторов символы,
      которые не являются частью стандартного синтаксиса CSS, то при поиске такие символы должны быть экранированы с помощью символа обратной косой черты ("\"). Поскольку обратная косая черта также является специальным символом (escape) в JavaScript,
      то при вводе литеральной строки ее необходимо экранировать дважды. Обязательный параметр.
    </p>
  </div>
</div>

<div class="accordion-card">
  <button class="accordion-title collapsed">
        Исключения
    </button>

  <div class="collapse">
    <p>
      Возникает в том случае, если синтаксис указанного селектора или селекторов некорректен.
    </p>
  </div>
</div>

<div class="accordion-card">
  <button class="accordion-title collapsed">
        Результат нашего примера:
    </button>

  <div class="collapse">
    <p>
      В этом примере с использованием атрибута событий onclick при нажатии на кнопку (HTML элемент) вызываем функцию myFunc(), которая с использованием JavaScript метода document.querySelectorAll() выбирает все элементы с классом block и инициализируем переменную
      этим значением (объект NodeList). После этого мы проходим циклом по всем элементам этого массивоподобного объекта (объект NodeList) и устанавливаем зеленый цвет текста каждому элементу.
    </p>
  </div>
</div>

→ Ссылка