Почему обработчик событий переключает имя класса только в первом элементе элемента, но не для остальных?

#javascript #event-handling #dom-events #toggle #accordion

Вопрос:

Я вижу, что класс активно добавляется в консоль для первого элемента, но только для первого элемента. Прослушиватель событий привязан к кнопке svg. Класс «открыть» превращает отображение «скрытого блока» в отображение: блок; в контейнере элементов

 const faqDropDown = document.querySelector(".accordion-icon");
const faqContainer = document.querySelector(".item");

faqDropDown.addEventListener("click", function() {
  faqContainer.classList.toggle("open");
}); 
 .item { margin-bottom: 10px; }
p { margin: 0; }
svg { width 1em; height: 1em; margin: 10px; } 
 <div class="item">
  <p class="number">01</p>
  <p class="text">lorem ipsum ist?</p>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    class="accordion-icon"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
  >
    <path
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      d="M19 9l-7 7-7-7"
    />
  </svg>

  <div class="hidden-box">
    <p>
      Lorem ipsum, dolor sit amet consectetur adipisicing elit.
      Et, tempore repudiandae. Minima, cupiditate totam nihil
      laborum rem sit, est, ea et quaerat hic accusantium quos.
    </p>
  </div>
</div>

<div class="item">
  <p class="number">02</p>
  <p class="text">lorem ipsum ist?</p>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    class="accordion-icon"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
  >
    <path
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      d="M19 9l-7 7-7-7"
    />
  </svg>

  <div class="hidden-box">
    <p>
      Lorem ipsum, dolor sit amet consectetur adipisicing elit.
      Et, tempore repudiandae. Minima, cupiditate totam nihil
      laborum rem sit, est, ea et quaerat hic accusantium quos.
    </p>
  </div>
</div> 

Ответ №1:

Оператору требуется обработка состояния всех элементов аккордеона. Таким образом, как и в приведенном примере, обработчик событий должен быть зарегистрирован для значка svg каждого элемента.

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

 function handleAccordionItemStates({ currentTarget }) {
  const currentItem = currentTarget.closest('.item');
  const currentAccordion = currentItem.closest('.accordion');

  const isCloseOthers = !currentItem.classList.contains('open');
  if (isCloseOthers) {

    currentAccordion
      .querySelectorAll('.item')
      .forEach(item => {
        if (item !== currentItem) {
          item.classList.remove('open');
        }
      });
  }
  currentItem.classList.toggle('open');
}

function init() {
  document
    .querySelectorAll('.accordion-icon')
    .forEach(elmNode =>
      elmNode.addEventListener('click', handleAccordionItemStates)
    );
}
init(); 
 p { margin: 0; }
svg {
  width 1em;
  height: 1em;
  transition: transform .3s ease-in;
}

.item { margin-bottom: 10px; }
.item p { float: left; margin-right: 10px; }
.item svg { float: right; margin-right: 50px; }

.hidden-box {
  clear: both;
  overflow: hidden;
  padding-top: 5px;
  transition: max-height .6s ease-in-out;
  max-height: 0;
}
.item.open .hidden-box { max-height: 900px; }
.item.open svg { transform: rotate(180deg); } 
 <div class="accordion">
  <div class="item">
    <p class="number">01</p>
    <p class="text">lorem ipsum</p>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      class="accordion-icon"
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="M19 9l-7 7-7-7"
      />
    </svg>

    <div class="hidden-box">
      <p>
        Lorem ipsum, dolor sit amet consectetur adipisicing elit. Et,
        tempore repudiandae. Minima, cupiditate totam nihil laborum rem sit,
        est, ea et quaerat hic accusantium quos.
      </p>
    </div>
  </div>

  <div class="item">
    <p class="number">02</p>
    <p class="text">accusantium quos</p>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      class="accordion-icon"
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="M19 9l-7 7-7-7"
      />
    </svg>

    <div class="hidden-box">
      <p>
        Lorem ipsum, dolor sit amet consectetur adipisicing elit. Et,
        tempore repudiandae. Minima, cupiditate totam nihil laborum rem sit,
        est, ea et quaerat hic accusantium quos.
      </p>
    </div>
  </div>

  <div class="item">
    <p class="number">03</p>
    <p class="text">cupiditate totam</p>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      class="accordion-icon"
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="M19 9l-7 7-7-7"
      />
    </svg>

    <div class="hidden-box">
      <p>
        Lorem ipsum, dolor sit amet consectetur adipisicing elit. Et,
        tempore repudiandae. Minima, cupiditate totam nihil laborum rem sit,
        est, ea et quaerat hic accusantium quos.
      </p>
    </div>
  </div>

</div> 

Ответ №2:

ваша проблема заключается в том, что вы используете querySelector , который вернет первый встреченный объект, это означает, что он вернет только первый существующий элемент .item .

Чтобы применить некоторые изменения к нескольким элементам с классом (я думаю , у вас их несколько class="item" ), вам нужно использовать querySelectorAll , что вернет массив узлов, у которых есть класс item .

ниже я оставил вам пример того, как использовать querySelectorAll , а затем выполнить цикл над каждым узлом, а затем применить изменения, которые вы хотите.

примечание: если вы посмотрите на код, html был создан мной только для того, чтобы показать, что он работает (он меняет все элементы на красный). часть javascript-это та, которая изменилась, чтобы внести правильные изменения во все элементы. просто скопируйте и вставьте его в свой код, и он должен работать.

 const faqDropDown = document.querySelector(".accordion-icon");
const faqContainers = document.querySelectorAll(".item");

faqDropDown.addEventListener("click", function() {
  faqContainers.forEach(faqContainer => {
    faqContainer.classList.toggle("open");
  })
}); 
 .open {
  color: red;
} 
 <button class="accordion-icon">
toggle open
</button>

<div class="item">
  item1
</div>

<div class="item">
  item2
</div>

<div class="item">
  item3
</div>

<div class="item">
  item4
</div> 

Комментарии:

1. Похоже на то, что я ищу! Однако я хочу, чтобы он открывал и закрывал только выпадающий список, на который я нажимаю. Как бы я это сделал?