0

I have created this toggle script which works well but I need to improve it so that onclick, any previously collapsed (i.e. open) subcats would simultaneously close and only the one clicked should collapse (i.e. open).

var collapse = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < collapse.length; i++) {
  collapse[i].addEventListener("click", function() {
    this.classList.toggle("activeCollapse");
    var content = this.nextElementSibling;
    if (content.style.maxHeight) {
      content.style.maxHeight = null;
    } else {
      content.style.maxHeight = content.scrollHeight + "px";
    }
  });
}
.catColumn {
  width: 400px;
  margin: 0 auto;
}

.collapsible {
  width: 100%;
  padding: 14px 8px;
  margin-top: 10px;
  color: #5a2e0f;
  background-color: #fff;
  border: 1px solid #c1bfbf;
  outline: none;
  border-radius: 6px;
}

.collapsible:hover {
  background-color: #efdac6;
}

.collapsible:after {
  float: right;
  content: '\002B';
  color: #5a2e0f;
  font-size: 1.8em;
  margin-left: 5px;
  cursor: pointer;
  line-height: 26px;
}

.activeCollapse {
  background-color: #efdac6;
  border-radius: 0;
  border-bottom: 0px;
}

.activeCollapse:after {
  content: "\2212";
}


/**/

ul.subcats {
  width: 100%;
  max-height: 0;
  transition: max-height 1s ease-out;
  overflow: hidden;
  font-size: 13px;
}

ul.subcats li {
  padding: 12px;
}
<div class="catColumn">
  <div class="collapsible">cat1</div>
  <ul class="subcats">
    <li>subcat1</li>
    <li>subcat2</li>
  </ul>
  <div class="collapsible">cat2</div>
  <ul class="subcats">
    <li>subcat3</li>
    <li>subcat4</li>
  </ul>
</div>
j08691
  • 204,283
  • 31
  • 260
  • 272
  • Just loop over ALL of the collapsible elements (regardless of their current open/closed state) and collapse them. Then, open just the one that was clicked. – Scott Marcus Jun 27 '22 at 17:21
  • Also, [don't use `getElementsByClassName`](https://stackoverflow.com/questions/54952088/how-to-modify-style-to-html-elements-styled-externally-with-css-using-js/54952474#54952474) especially with loops. Use `querySelectorAll` instead. – Scott Marcus Jun 27 '22 at 17:23

3 Answers3

2

   var collapse = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < collapse.length; i++) {
  collapse[i].addEventListener("click", function() {
    this.classList.toggle("activeCollapse");
for (j = 0; j < collapse.length; j++) {
    this.classList.remove("activeCollapse");
    if(j!=i)
    collapse[j].nextElementSibling.style.maxHeight = null;
    var content = this.nextElementSibling;
  }
    if (content.style.maxHeight) {
      content.style.maxHeight = null;
    } else {
      content.style.maxHeight = content.scrollHeight + "px";
    }
  });
}
    .catColumn {
  width: 400px;
  margin: 0 auto;
}

.collapsible {
  width: 100%;
  padding: 14px 8px;
  margin-top: 10px;
  color: #5a2e0f;
  background-color: #fff;
  border: 1px solid #c1bfbf;
  outline: none;
  border-radius: 6px;
}

.collapsible:hover {
  background-color: #efdac6;
}

.collapsible:after {
  float: right;
  content: '\002B';
  color: #5a2e0f;
  font-size: 1.8em;
  margin-left: 5px;
  cursor: pointer;
  line-height: 26px;
}

.activeCollapse {
  background-color: #efdac6;
  border-radius: 0;
  border-bottom: 0px;
}

.activeCollapse:after {
  content: "\2212";
}


/**/

ul.subcats {
  width: 100%;
  max-height: 0;
  transition: max-height 1s ease-out;
  overflow: hidden;
  font-size: 13px;
}

ul.subcats li {
  padding: 12px;
}
 <div class="catColumn">
        <div class="collapsible">cat1</div>
        <ul class="subcats">
          <li>subcat1</li>
          <li>subcat2</li>
        </ul>
        <div class="collapsible">cat2</div>
        <ul class="subcats">
          <li>subcat3</li>
          <li>subcat4</li>
        </ul>
      </div>
  • Thanks for trying but the plus and minus no longer toggle as they did before. Additionally there is always a cat that's left collapsed (open) with your script. – user3589574 Jun 27 '22 at 18:29
1

First, loop through the open elements and remove the class and set max height to null. Then do your normal code.

I also changed your event Listener so you only have one instead of one for each element.

var collapse = document.querySelector(".catColumn");

collapse.addEventListener("click", function(e) {
  let el = e.target;
  let hideSelf = (el.className.indexOf("activeCollapse") > 0);

  if (el.className.indexOf("collapsible") > -1) {
    let shown = document.querySelectorAll(".activeCollapse");
    shown.forEach(function(activeEl) {
      activeEl.classList.remove("activeCollapse");
      activeEl.nextElementSibling.style.maxHeight = null;
    });

    if (hideSelf == false) {
      el.classList.toggle("activeCollapse");
      var content = el.nextElementSibling;
      if (content.style.maxHeight) {
        content.style.maxHeight = null;
      } else {
        content.style.maxHeight = content.scrollHeight + "px";
      }
    }


  }
});
.catColumn {
  width: 400px;
  margin: 0 auto;
}

.collapsible {
  width: 100%;
  padding: 14px 8px;
  margin-top: 10px;
  color: #5a2e0f;
  background-color: #fff;
  border: 1px solid #c1bfbf;
  outline: none;
  border-radius: 6px;
}

.collapsible:hover {
  background-color: #efdac6;
}

.collapsible:after {
  float: right;
  content: '\002B';
  color: #5a2e0f;
  font-size: 1.8em;
  margin-left: 5px;
  cursor: pointer;
  line-height: 26px;
}

.activeCollapse {
  background-color: #efdac6;
  border-radius: 0;
  border-bottom: 0px;
}

.activeCollapse:after {
  content: "\2212";
}


/**/

ul.subcats {
  width: 100%;
  max-height: 0;
  transition: max-height 1s ease-out;
  overflow: hidden;
  font-size: 13px;
}

ul.subcats li {
  padding: 12px;
}
<div class="catColumn">
  <div class="collapsible">cat1</div>
  <ul class="subcats">
    <li>subcat1</li>
    <li>subcat2</li>
  </ul>
  <div class="collapsible">cat2</div>
  <ul class="subcats">
    <li>subcat3</li>
    <li>subcat4</li>
  </ul>
</div>
imvain2
  • 15,480
  • 1
  • 16
  • 21
  • Thank you for trying. Although the collapsibles seem to work (i.e. when one is opened the other ones close ok as expected if clicked), can you fix the problem of the toggle on the last open collapsible which does not close any longer when clicked on the minus. – user3589574 Jun 29 '22 at 22:15
  • @user3589574 I have updated my answer to check if the element clicked is shown, if it is, don't show it after hiding. – imvain2 Jun 29 '22 at 22:26
  • Excellent. Thank you very much. If I need to apply it to 3 separate Columns of catColumn how do I loop it so that it all works. – user3589574 Jun 30 '22 at 03:51
  • Further to yesterday's comments, I looped the catColumn and it seems to work: var collapse = document.querySelectorAll(".catColumn"); for (var c=0; c < collapse.length; c++) { your script }; Can this be improved? Thanks again – user3589574 Jun 30 '22 at 15:08
0

var parentDiv = document.getElementsByClassName("catColumn")[0];
parentDiv.addEventListener("click",function(e){
let current= e.target;

var collapse = document.getElementsByClassName("collapsible");
let toExpand = !current.nextElementSibling.style.maxHeight ;
for (let i = 0; i < collapse.length; i++) {          
    var content = collapse[i].nextElementSibling;
    if (content.style.maxHeight) {
      content.style.maxHeight = null;
      collapse[i].classList.toggle("activeCollapse");
    }
  }
  let toToggle = current.nextElementSibling;
   if(toExpand ){
    toToggle.style.maxHeight = content.scrollHeight + "px";
     current.classList.toggle("activeCollapse");
    }
  
});
.catColumn {
  width: 400px;
  margin: 0 auto;
}

.collapsible {
  width: 100%;
  padding: 14px 8px;
  margin-top: 10px;
  color: #5a2e0f;
  background-color: #fff;
  border: 1px solid #c1bfbf;
  outline: none;
  border-radius: 6px;
}

.collapsible:hover {
  background-color: #efdac6;
}

.collapsible:after {
  float: right;
  content: '\002B';
  color: #5a2e0f;
  font-size: 1.8em;
  margin-left: 5px;
  cursor: pointer;
  line-height: 26px;
}

.activeCollapse {
  background-color: #efdac6;
  border-radius: 0;
  border-bottom: 0px;
}

.activeCollapse:after {
  content: "\2212";
}


/**/

ul.subcats {
  width: 100%;
  max-height: 0;
  transition: max-height 1s ease-out;
  overflow: hidden;
  font-size: 13px;
}

ul.subcats li {
  padding: 12px;
}
<div class="catColumn">
  <div class="collapsible">cat1</div>
  <ul class="subcats">
    <li>subcat1</li>
    <li>subcat2</li>
  </ul>
  <div class="collapsible">cat2</div>
  <ul class="subcats">
    <li>subcat3</li>
    <li>subcat4</li>
  </ul>
</div>
gvmani
  • 1,580
  • 1
  • 12
  • 20