Bootstrap 4 change accordion behaviour

Viewed 56

I'm using HTML templates, which are based on Bootstrap 4.3.1 to present my students with learning content. In the current templates, all accordion panels are closed on page load and each accordion panel can be opened regardless of how many others have also been opened.

A working example can be found on this CodePen: https://codepen.io/hagelslag1001/pen/MWGeZJr

The HTML code is as follows:

<h2>Accordion: Group of 2</h2>
<p class="small">Accordion: start copy</p>
<!-- Accordion headings should be changed to respect page hierarchy -->
<div class="accordion shadow mb-5">
   <div class="card">
      <div class="card-header">
         <h2 class="card-title">
            Accordion 1 of 2
         </h2>
      </div>
      <div class="collapse">
         <div class="card-body">
            <p>Insert Accordion 1 of 2 content here.</p>
         </div>
      </div>
   </div>
   <div class="card">
      <div class="card-header">
         <h2 class="card-title">
            Accordion 2 of 2
         </h2>
      </div>
      <div class="collapse">
         <div class="card-body">
            <p>Insert Accordion 2 of 2 content here.</p>
         </div>
      </div>
   </div>
</div>
<p class="small">Accordion: end copy</p>

Is it possible to change this default behaviour so that only one panel can be opened at a time? (i.e. as soon as a new panel is opened, the previously opened panel will automatically close)

The Bootstrap examples use (data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne") to accomplish this for the accordions in these templates

I can't figure out how to accomplish this, since the HTML code for the accordions in these templates look different than the Bootstrap 4 examples, which use either an a or button tag to trigger the collapsible event.

2 Answers

You can do this by letting javascript check for active classes and only allowing one at the time. Here's a simplified example.

// DOM here
let nav = document.querySelector(".nav");

// Handlers here
const clickHandler = function (e) {
  if (e.target.classList.contains("nav__link")) {
    const link = e.target;
    const siblings = link.closest(".nav").querySelectorAll(".nav__link");

    link.classList.toggle("active");

    // removes all actives except for the clicked one
    siblings.forEach((el) => {
      if (el !== link) el.classList.remove("active");
    });
  }
};

// Listeners here
nav.addEventListener("click", clickHandler);
body {
  font-family: sans-serif;
}

.nav__link {
  display: block;
  width: 100%;
}
.active {
  background: #0f0;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div class="nav">
      <ul class="nav__links">
                <li class="nav__item">
                    <a class="nav__link" href="#accordeon--1">Accordeon 1</a>
                </li>
                <li class="nav__item">
                    <a class="nav__link" href="#accordeon--2">Accordeon 2</a>
                </li>
                <li class="nav__item">
                    <a class="nav__link" href="#accordeon--3">Accordeon 3</a>
                </li>
                <li class="nav__item">
                    <a class="nav__link" href="#accordeon--4"
                        >Accordeon 4</a
                    >
                </li>
            </ul>
      </div>
    </div>

    <script src="src/index.js"></script>
  </body>
</html>

Related