Home > Blockchain >  Make accordion item open by default
Make accordion item open by default

Time:09-27

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>

  • Related