Why does the event handler toggle the class name just at the first item element but not for the rest?

Issue

I see the class being actively added in the console for the first element, but only for the first element. The event listener is tied to an svg button. Class ‘open’ turns ‘hidden-block’ display into display: block; in the item container

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>

Solution

The OP needs a state handling across all of an accordion’s items. Thus, as for the provided example, an event handler has to be registered for every item’s svg icon.

The handler then not only needs to take care of an item’s toggle state but also needs to detect and process the case of closing all other items …

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>

Answered By – Peter Seliger

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published