In my accordion set I'm trying to get the first accordion item open by default on page load.
I'm adding the class .open to that element, like such:
<button >
This seems to work but the problem is when I click on that same element instead of closing it, it keeps re-opening it and by looking at the JS script I don't quite understand what could be the problem.
Demo
var accordionButton = document.querySelectorAll("button");
for (var i = 0; i < accordionButton.length; i ) {
accordionButton[i].addEventListener("click", switchClasses);
}
function switchClasses() {
for (var i = 0; i < accordionButton.length; i ) {
if (this !== accordionButton[i]) {
accordionButton[i].classList.remove("open");
accordionButton[i].nextElementSibling.style.maxHeight = null;
}
}
this.classList.toggle("open");
var nextAccordionButton = this.nextElementSibling;
if (nextAccordionButton.style.maxHeight) {
nextAccordionButton.style.maxHeight = null;
} else {
nextAccordionButton.style.maxHeight =
nextAccordionButton.scrollHeight "px";
}
}
.accordion-item {
border: 1px solid lightgrey;
}
button {
background: none;
border: none;
width: 100%;
max-width: none;
height: auto;
padding: 12px;
text-align: left;
cursor: pointer;
transition: 0.5s;
}
.content-wrapper {
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.content {
padding: 0 10px;
}
.open {
background: lightgray;
border-bottom: none;
}
.open .content-wrapper {
max-height: none;
}
<div >
<button >
<span >Accordion 1</span>
</button>
<div >
<div >
<p>Accordion 1 content.
</div>
</div>
</div>
<div >
<button>
<span >Accordion 2</span>
</button>
<div >
<div >
<p>Accordion 2 content.
</div>
</div>
</div>
<div >
<button>
<span >Accordion 3</span>
</button>
<div >
<div >
<p>Accordion 3 content.
</div>
</div>
</div>
CodePudding user response:
You also have to check for the currently clicked element. What happened was that you were constantly re-applying open
to the element right after removing it.
Also, try to use const
for variables that wont be re-assigned. and let
for variables that will be mutated.
var
keyword is not advised.
const accordionButton = document.querySelectorAll('button');
for (let i = 0; i < accordionButton.length; i ) {
accordionButton[i].addEventListener('click', switchClasses);
}
function switchClasses() {
for (let i = 0; i < accordionButton.length; i ) {
if (this !== accordionButton[i]) {
accordionButton[i].classList.remove('open');
accordionButton[i].nextElementSibling.style.maxHeight = null;
continue;
}
if (this === accordionButton[i]) {
if (!this.classList.contains('open')) {
this.classList.add('open');
const nextAccordionButton = this.nextElementSibling;
nextAccordionButton.style.maxHeight =
nextAccordionButton.scrollHeight 'px';
} else {
accordionButton[i].classList.remove('open');
accordionButton[i].nextElementSibling.style.maxHeight = null;
}
}
}
}
.accordion-item {
border: 1px solid lightgrey;
}
button {
background: none;
border: none;
width: 100%;
max-width: none;
height: auto;
padding: 12px;
text-align: left;
cursor: pointer;
transition: 0.5s;
}
.content-wrapper {
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.content {
padding: 0 10px;
}
.open {
background: lightgray;
border-bottom: none;
}
.open .content-wrapper {
max-height: none;
}
<div >
<button >
<span >Accordion 1</span>
</button>
<div >
<div >
<p>Accordion 1 content.</p>
</div>
</div>
</div>
<div >
<button>
<span >Accordion 2</span>
</button>
<div >
<div >
<p>Accordion 2 content.</p>
</div>
</div>
</div>
<div >
<button>
<span >Accordion 3</span>
</button>
<div >
<div >
<p>Accordion 3 content.</p>
</div>
</div>
</div>
CodePudding user response:
Here is a simple accordion example to simplify your problem/structure.
On click, I remove open
if the clicked element has the class. Else, I remove the class if an accordion is opened, then I open the clicked accordion.
document.querySelectorAll("button").forEach((e) => {
e.addEventListener("click", () => {
if (e.classList.contains("open")) {
e.classList.remove("open")
} else {
let opened = document.querySelector("button.open")
if (opened) {
opened.classList.remove("open")
}
e.classList.add("open")
}
})
});
.content {
display: none;
}
button {
display: block;
}
.open {
background-color: orange;
}
.open .content {
display: block;
}
<button >Accordion 1</button>
<div >Content 1</div>
<button>Accordion 2</button>
<div >Content 2</div>
<button>Accordion 3</button>
<div >Content 3</div>