0

I'm using this code from W3Schools (https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_accordion_symbol) but I need for only one accordion to be open at a time. And as you can see from the example, there can be an indefinite amount of open accordions at a time.

I'm thinking it can be fixed just by adding another if condition in the javascript but I'm not too familiar with JavaScript to figure it out.

Can anybody help me?

HTML:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h2>Accordion with symbols</h2>
<p>In this example we have added a "plus" sign to each button. When the user clicks on the button, the "plus" sign is replaced with a "minus" sign.</p>
<button class="accordion">Section 1</button>
<div class="panel">
  <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 class="accordion">Section 2</button>
<div class="panel">
  <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 class="accordion">Section 3</button>
<div class="panel">
  <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>
</body>
</html>

CSS:

.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active, .accordion:hover {
  background-color: #ccc;
}

.accordion:after {
  content: '\002B';
  color: #777;
  font-weight: bold;
  float: right;
  margin-left: 5px;
}

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

.panel {
  padding: 0 18px;
  background-color: white;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
}

JS:

var acc = document.getElementsByClassName("accordion");
var i;

for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var panel = this.nextElementSibling;
    if (panel.style.maxHeight) {
      panel.style.maxHeight = null;
    } else {
      panel.style.maxHeight = panel.scrollHeight + "px";
    } 
  });
}
Tadeo Lemus
  • 25
  • 1
  • 8

1 Answers1

2

In the code that opens the desired pane, insert (just before the open code) a loop that goes through all the panels and closes them.

var acc = document.querySelectorAll(".accordion");
var i;

for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
  
    // Loop over all the panels an close each one
    document.querySelectorAll(".panel").forEach(function(panel){
      panel.style.maxHeight = "0";
      panel.previousElementSibling.classList.remove("active");
    });
  
    // Then show the clicked panel
    this.classList.toggle("active");
    var panel = this.nextElementSibling;
    panel.style.maxHeight = panel.scrollHeight + "px";

  });
}
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active, .accordion:hover {
  background-color: #ccc;
}

.accordion:after {
  content: '\002B';
  color: #777;
  font-weight: bold;
  float: right;
  margin-left: 5px;
}

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

.panel {
  padding: 0 18px;
  background-color: white;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h2>Accordion with symbols</h2>
<p>In this example we have added a "plus" sign to each button. When the user clicks on the button, the "plus" sign is replaced with a "minus" sign.</p>
<button class="accordion">Section 1</button>
<div class="panel">
  <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 class="accordion">Section 2</button>
<div class="panel">
  <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 class="accordion">Section 3</button>
<div class="panel">
  <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>
</body>
</html>

With that said, W3Schools is well-known to be a terrible resource as it often has incomplete, out-of-date, or flat out wrong information. Case in point, .getElementsByClasName() is a 25+ year old out-dated API that really should not be used in 2019. Use .querySelectorAll() instead. Also, working with inline styles and an element's .style property is strongly discouraged. Instead, use pre-made CSS classes.

Here's the modern way to do this (note that a new div has been added to wrap the entire accordion and each button has had its class changed to section):

// Set up the click event on the accordion itself
// When a panel is clicked, that event will bubble up
// to the accordion and can be handled there.
document.querySelector(".accordion").addEventListener("click", function(event){

    // Check to see if the clicked panel was the currently open one.
    let alreadyActive = event.target.classList.contains("active");
    
    // Loop over all the panels an close each one
    document.querySelectorAll(".panel").forEach(function(panel){
      panel.style.maxHeight = "0";
      panel.previousElementSibling.classList.remove("active");
    });
  
    // If the clicked panel wasn't the already active one go ahead
    // and open the clicked panel. Otherwise, do nothing and leave
    // all the panels closed.
    if(!alreadyActive){
      // Then show the clicked panel which is accessible as event.target
      event.target.classList.add("active");
      var panel = event.target.nextElementSibling;
      panel.style.maxHeight = panel.scrollHeight + "px";
    }

});
.section {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  transition: 0.4s;
}

.active, .section:hover {
  background-color: #ccc;
}

.section:after {
  content: '\002B';
  color: #777;
  font-weight: bold;
  float: right;
  margin-left: 5px;
}

.section.active:after {
  content: "\2212";
}

.panel {
  padding: 0 18px;
  background-color: white;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h2>Accordion with symbols</h2>
<p>In this example we have added a "plus" sign to each button. When the user clicks on the button, the "plus" sign is replaced with a "minus" sign.</p>

<div class="accordion">
  <button class="section">Section 1</button>
  <div class="panel">
    <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 class="section">Section 2</button>
  <div class="panel">
    <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 class="section">Section 3</button>
  <div class="panel">
    <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>
</body>
</html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • That's awesome, thanks a lot man! Do you have a recommendation for something else besides W3Schools? I wasn't aware it wasn't a good source. – Tadeo Lemus Dec 08 '19 at 21:18
  • You're welcome. Yes, click on the `.querySelectorAll()` link in my answer and you'll be taken to the Mozilla Developer's Network (MDN), which is "the" authoritative source for JavaScript. Don't forget to up vote the answer and to mark it as "the" answer by clicking the check mark at the top-left of it. Good luck! – Scott Marcus Dec 08 '19 at 21:20
  • Awesome, I'll check it out jquery to learn more about JavaScript. Also, do you know how to be able to close out the accordion, to have the option of not having any open accordions if needed? – Tadeo Lemus Dec 08 '19 at 21:30
  • @EfrainLemus Answer updated to include closing the already open panel by clicking on it again. Also, my advice is NOT to bother with JQuery until you have a solid grasp of pure JavaScript (I have been a professional IT trainer for over 25+ years, trust me). – Scott Marcus Dec 08 '19 at 22:35
  • You're the best! Thanks a lot! And I'll keep that in mind. I'm currently taking this JavaScript course https://www.udemy.com/share/101qIyAksecV5WRnw=/. I know it won't make me an expert but at least I'll get started with it. @ScottMarcus I upvoted your answer but since I have less than 15 reputation points, it's not publicly changed. – Tadeo Lemus Dec 08 '19 at 22:46
  • You're welcome. You can click the check mark at the top-left of my answer to mark it as "the" answer. – Scott Marcus Dec 09 '19 at 01:28
  • I have a question related to this, but it's for a sub-navigation menu. It's for this website https://crown-cabinetry.myshopify.com/. If you notice on mobile, the sub-navigation menus are similar to the accordion. I'm wondering if you can help me with this? I don't know how much you charge per hour but I'm willing to pay. I really need help with this for the end of this week. p.s. Happy New Year! – Tadeo Lemus Dec 31 '19 at 23:50