I have an accordion that works really well, it looks good on the site and works as it should. However, I'm trying to add some more JavaScript functionality to it, to make it more it look more professional.
Currently, the accordion allows you to have multiple panels open at one time i.e. if I open one tab, and then open another tab, both tabs will be open at the same time. And the only way to close these panels, is to re-click on the header.
What I would like is some JavaScript code that prevents multiple tabs from being open at one time, so if I click on a new panel, it should close the existing open panel first. Here is my HTML code for the accordion:
<div class="accordion"><b>Heading 1</b></div>
<div class="panel">
<p class="text-light">Text 1</p>
</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel">
<p class="text-light">Text 2</p>
</div>
Here is my JavaScript code in a separate JavaScript file which currently allows multiple tabs to be open at one time
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
Not sure if you need all the CSS, but heres the CSS for showing the panel
div.panel.show {
display: block !important;
}
Hopefully someone can help! Thanks in advance!
To achieve this you need to reset the state of the accordion back to its original state on each click, before you set the required classes on the clicked elements. To do that you can extract functionality to set the class names in to their own function and call it as required. Try this:
var acc = document.getElementsByClassName("accordion");
var panel = document.getElementsByClassName('panel');
for (var i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var setClasses = !this.classList.contains('active');
setClass(acc, 'active', 'remove');
setClass(panel, 'show', 'remove');
if (setClasses) {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
}
function setClass(els, className, fnName) {
for (var i = 0; i < els.length; i++) {
els[i].classList[fnName](className);
}
}
Working example
Close all opened tabs before opening selected tab. This will prevent multiple tabs from being open at one time.
// Get all Accordion and Panel
let accHeading = document.querySelectorAll(".accordion");
let accPanel = document.querySelectorAll(".accordion-panel");
for (let i = 0; i < accHeading.length; i++) {
// Execute whenever an accordion is clicked
accHeading[i].onclick = function() {
if (this.nextElementSibling.style.maxHeight) {
hidePanels(); // Hide All open Panels
} else {
showPanel(this); // Show the panel
}
};
}
// Function to Show a Panel
function showPanel(elem) {
hidePanels();
elem.classList.add("active");
elem.nextElementSibling.style.maxHeight = elem.nextElementSibling.scrollHeight + "px";
}
// Function to Hide all shown Panels
function hidePanels() {
for (let i = 0; i < accPanel.length; i++) {
accPanel[i].style.maxHeight = null;
accHeading[i].classList.remove("active");
}
}
* {box-sizing: border-box;}
/* Style the Headings that are used to open and close the accordion panel */
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
margin: 0;
font-weight: 300;
}
/* Change color of the heading and icon (on hover and click) */
.active, .accordion:hover, .accordion:hover::after {
background-color: #007eff;
color: white;
}
/* Add "plus" sign (+) after Accordion */
.accordion::after {
content: '\002B';
color: #777;
font-weight: bold;
float: right;
}
/* Add "minus" sign (-) after Accordion (when accordion is active) */
.active::after {
content: "\2212";
color: white;
}
/* Style the accordion panel */
.accordion-panel {
padding: 0 18px;
overflow: hidden;
max-height: 0;
transition: max-height 0.2s ease-out;
}
<div style="border: 1px solid lightgray;">
<h2 class="accordion">Section 1</h2>
<div class="accordion-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>
<h2 class="accordion">Section 2</h2>
<div class="accordion-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>
<h2 class="accordion">Section 3</h2>
<div class="accordion-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>
<h2 class="accordion">Section 4</h2>
<div class="accordion-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>
When selecting one item, you just need to hide all of them beforehand.
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
hideAll();
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
function hideAll() {
for (i = 0; i < acc.length; i++) {
acc[i].classList.toggle("active", false);
acc[i].nextElementSibling.classList.toggle("show", false);
}
}
var acc = document.getElementsByClassName("accordion");
var i;
var last;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
if(last){
last.classList.toggle("active",false);
last.nextElementSibling.classList.toggle("show",false);
}
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
last=this;
}
}
variable last will track the last active accordion, so you don't need to iterate every accordion and panel again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With