Pixema
Pixema

Reputation: 9

I am trying to make a collapsable menu with only 1 open at a time pure javascript

Here is my code and the JSfiddle code, I am having an issue now that if one is opened and you try to close it again it won't close.

https://jsfiddle.net/k68z3aLv/

var coll = document.getElementsByClassName("collapsible");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
 
	  var b;
	  for(b = 0; b < coll.length; b++) {
     
	  if(coll[b] != coll[i]) {
		  if(coll[b].getAttribute('id') == 'submenuopened')
		  {
			   coll[b].classList.remove("active");
			  coll[b].setAttribute("id",'');
			   var content3 = coll[b].nextElementSibling;
			    content3.style.display = "none";
             
			// sleep(0);
	  }
	  }
	  }
    
    this.classList.toggle("active");
	if(this.getAttribute('id') == 'submenuopened') {
		this.setAttribute("id",'ha');
	} else { this.setAttribute("id", "submenuopened"); }
	
    var content = this.nextElementSibling;
    if (content.style.display == 'block'){
      content.style.display = "none";
    } else {
      content.style.display = "block";
    } 
  });
}
.collapsible {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

.active, .collapsible:hover {
  background-color: #555;
}

.content {
  padding: 0 18px;
  display: none;
  overflow: hidden;
  background-color: #f1f1f1;
}
<button type="button" class="collapsible">Open Section 1</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 2</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 3</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

Upvotes: 0

Views: 69

Answers (3)

Ivan Karaman
Ivan Karaman

Reputation: 1234

I little bit optimized your code jsfiddle, id was removed because active class is enough, open/close moved to CSS because it's a better way to do it, extra code was removed to look it more clear

var coll = document.getElementsByClassName("collapsible");

for (let i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
	  for(let n = 0; n < coll.length; n++) {
      if(coll[n] == this) continue;
      coll[n].classList.remove("active");
	  }
    
    this.classList.toggle("active"); 
  });
}
.collapsible {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

.active, .collapsible:hover {
  background-color: #555;
}

.collapsible.active + .content {
  display: block;
}

.content {
  padding: 0 18px;
  display: none;
  overflow: hidden;
  background-color: #f1f1f1;
}
<button type="button" class="collapsible">Open Section 1</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 2</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 3</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

Upvotes: 1

Sunyatasattva
Sunyatasattva

Reputation: 5840

There is actually a much simpler way to iterate through your DOM elements and also to make your code more understandable.

Working example

These are the edits required. HTML and CSS are very small adjustments, the JS part is the interesting one.

HTML

<div class="collapsible">
  <header>
    Open Section 1
  </header>
  <div class="content">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
  </div>
</div>

...

Reasoning

Unless your buttons to open and close the collapsibles are far away from the actual content (which is not the case you mentioned here), semantically it's good for the whole element to be grouped together.

CSS


.collapsible header {
  ...
}

.collapsible.is-active .content {
  display: block;
}

Reasoning

The only change required here is adjusting the CSS to target the header element, as the .collapsible class is now applied to the parent.

Also, we add a simple semantic class that will tell us which one is currently active, and show it.

JS

const collapsibles = Array.from(document.getElementsByClassName("collapsible"));

function toggleVisibility(e) {
  const parent = e.target.parentElement;

  parent.classList.toggle("is-active");

  collapsibles.forEach(collapsible => {
    if(collapsible === parent) return;

    collapsible.classList.remove("is-active")
  });
}

collapsibles.forEach(collapsible => {
    collapsible.querySelector("header").addEventListener("click", toggleVisibility);
});

Reasoning

  1. First you get an array of parent DOM elements and store those into a variable. You will use that to decide which one to show and which one to hide.
  2. On each of those DOM elements, you attach your event listener using simpler and more readable Array methods instead of for loops.
  3. You refactor your handler into a simple function you can reuse. All that this function does, is make sure you toggle the current active one if you click on itself, and close all the rest.

Upvotes: 1

Joe Iddon
Joe Iddon

Reputation: 20434

Your code was quite confusing for me to decipher, so I just re-wrote it in what I would consider a clear way. This was achieved by creating two functions to open or close the submenu for a particular element.

var buttoncolumn = document.getElementsByClassName('collapsible');

function closesubmenu(el) {
  el.setAttribute('id', '');
  el.classList.remove('active');
  el.nextElementSibling.style.display = 'none';
}

function opensubmenu(el) {
  el.setAttribute('id', 'submenuopened');
  el.classList.add('active');
  el.nextElementSibling.style.display = 'block';
}

for (let button of buttoncolumn) {
  button.addEventListener('click', function() {
    if (this.getAttribute('id') == 'submenuopened') {
      closesubmenu(this);
    } else {
      for (let other of buttoncolumn) {
        closesubmenu(other);
      }
      opensubmenu(this);
    }
  });
};
.collapsible {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

.active,
.collapsible:hover {
  background-color: #555;
}

.content {
  padding: 0 18px;
  display: none;
  overflow: hidden;
  background-color: #f1f1f1;
}
<button type="button" class="collapsible">Open Section 1</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 2</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button type="button" class="collapsible">Open Section 3</button>
<div class="content">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

Upvotes: 0

Related Questions