javascriptfunctiononclicktoggleaccordion

Why does this collapsible need 2 clicks to open, then 2 more clicks to close?


Description

I'm having an issue with a collapsible requiring 2 clicks of a button to open, then another 2 clicks to close. The Show/Hide text inside the button gets mixed up as well. I think it might be caused by something being in the wrong order, but I haven't been able to find out exactly what. Everything I've tried either ends up causing the same problem or making the button stop working entirely. I'm somewhat new to using JS, and right now I'm very confused. How do I fix this?

Expected behavior

Collapsible opens when button is clicked once, then closes when button is clicked a second time. Button should say "Show" when closed, and "Hide" when open.

Actual behavior

Button is clicked once, resulting in text changing to "Hide". Collapsible remains closed.

Button is clicked again, resulting in text changing to "Show". Collapsible opens up.

Button is clicked a third time, resulting in text changing to "Hide" Collapsible remains open.

Button is clicked a fourth time, resulting in text changing to "Show". Collapsible closes.

Some answers I've looked at/tried to use

1, 2, 3, 4

Code

Fiddle

function toggle(e) {
  let txt = e.innerText;
  e.innerText = txt == 'Show' ? 'Hide' : 'Show';
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.maxHeight){
      content.style.maxHeight = null;
    } else {
      content.style.maxHeight = content.scrollHeight + "px";
    }
  });
}}
.content {
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
<button class="collapsible" onclick="toggle(this)">
Show
</button>
<div class="content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vel felis eu purus interdum egestas. Aliquam pharetra accumsan pellentesque. Mauris tristique mattis venenatis. Fusce lobortis arcu sit amet velit fermentum, vitae egestas ipsum auctor. Etiam interdum malesuada risus at dapibus. Nam sem urna, ultricies nec metus et, venenatis tincidunt felis. Vestibulum scelerisque orci sed libero ultricies consequat. Vivamus eget tristique urna.
</div>


Solution

  • I simplified your function a bit:

    function toggle(button) {
      let content = button.nextElementSibling;
      button.innerText = (button.innerText == 'Show') ? 'Hide' : 'Show';
      content.style.maxHeight =  content.style.maxHeight ? null : content.scrollHeight + "px";
    }
    .content {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.2s ease-out;
    }
    <button class="collapsible" onclick="toggle(this)">
    Show
    </button>
    <div class="content">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vel felis eu purus interdum egestas. Aliquam pharetra accumsan pellentesque. Mauris tristique mattis venenatis. Fusce lobortis arcu sit amet velit fermentum, vitae egestas ipsum auctor. Etiam interdum malesuada risus at dapibus. Nam sem urna, ultricies nec metus et, venenatis tincidunt felis. Vestibulum scelerisque orci sed libero ultricies consequat. Vivamus eget tristique urna.
    </div>

    I am not sure what is going on inside your toggle() function. You supply the button as an argument to that function, so why find it again? Adding the event listener inside the function only added to the confusion.