barciewicz
barciewicz

Reputation: 3813

Event listener gets attached inconsequently

Can someone explain why does clicking on second "Show details" will not work unless previous "Show details" is already clicked (except for the first "Show details", this will always work)? How can I make every toggle work, regardless of clicking order (ideally, keeping the for loop).

const toggles = document.querySelectorAll('.details-toggle')
toggles.forEach(toggle => {
  toggle.addEventListener('click', e => {
    const parent = e.target.parentElement
    const details = parent.nextElementSibling
    let opacity = details.style.opacity === "0" ? "1" : "0"
    let maxHeight = details.style.maxHeight === "0px" ? "1000px" : "0px"
    details.style.opacity = opacity
    details.style.maxHeight = maxHeight
  })
})
<!DOCTYPE html>
<html>

<body>

  <head>
  </head>

  <body>

    <ul>
      <li>
        <div>
          Title
          <small class="details-toggle">Show details</small>
        </div>
        <div class="details" style="opacity: 0; max-height: 0px">
          <ul>
            <li>Det 1</li>
            <li>Det 2</li>
          </ul>
        </div>
      </li>

      <li>
        <div>
          Title
          <small class="details-toggle">Show details</small>
        </div>
        <div class="details" style="opacity: 0; max-height: 0px">
          <ul>
            <li>Det 1</li>
            <li>Det 2</li>
          </ul>
        </div>
      </li>

      <li>
        <div>
          Title
          <small class="details-toggle">Show details</small>
        </div>
        <div class="details" style="opacity: 0; max-height: 0px">
          <ul>
            <li>Det 1</li>
            <li>Det 2</li>
          </ul>
        </div>
      </li>
    </ul>

  </body>

</html>

Upvotes: 2

Views: 59

Answers (1)

Jamiec
Jamiec

Reputation: 136164

It has nothing to do with your loop. It has to do with the fact that by just changing the opacity of your "hidden" elements they are still there, and actually overlay your other 2 "show details" elements so they never receive the click.

You can fix this by changing the display to none and then they actually do receive the click.

const toggles = document.querySelectorAll('div .details-toggle')
toggles.forEach(toggle => {
  toggle.addEventListener('click', e => {
    const parent = e.target.parentElement
    const details = parent.nextElementSibling
    console.log(details.style.display);
  })
})
<ul>
  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details" style="display:none">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>

  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details" style="display:none">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>

  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details" style="display:none">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>
</ul>

I would suggest you control the visibility of these detail elements by toggling a class to show/hide:

const toggles = document.querySelectorAll('div .details-toggle')
toggles.forEach(toggle => {
  toggle.addEventListener('click', e => {
    const parent = e.target.parentElement
    const details = parent.nextElementSibling
    details.classList.toggle("hidden");
  })
})
.hidden{
   display:none
}
<ul>
  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details hidden">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>

  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details hidden">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>

  <li>
    <div>
      Title
      <small class="details-toggle">Show details</small>
    </div>
    <div class="details hidden">
      <ul>
        <li>Det 1</li>
        <li>Det 2</li>
      </ul>
    </div>
  </li>
</ul>

Upvotes: 3

Related Questions