javascripthtmlaccordion

Only open one accordion tab at one time


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!


Solution

  • 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