Search code examples
javascriptjquerycsstwitter-bootstrapbootstrap-accordion

Set color of open Bootstrap 4 accordion panel on page loads


I want to highlight the panel which has been opened by the user. When a user clicks the button in the card-header the background is set to red. This works without any fuss, as shown in the snippet.

In some cases I'll have a panel, usually the first, open on page load, which I set with the class show. My goal is the set the background of this card-header to red as well on page load.

The buttons in the card-headers are set using js, as shown in the code snippet.

I reckon I have to use javascript to toggle classes.

First I added a new class highlight and added the following CSS code:

.accordion .card-header.highlight {
    color: #fff;
    background-color: red;
}

Then I added the following js (please be aware that I find js quite difficult to comprehend, albeit slowly but surely I'm learning) to try set the background of the header of the open pane on page load.

$(document).ready(function(){
    // Add class highlight to panel with show class
    $(".collapse.show").each(function(){
        $(this).prev(".card-header").addClass("highlight");
    });
    
    // toggle highlight class
    $(".card-header .btn").click(function(){
        $(".card-header").toggleClass("highlight");
    });
});

I looks very messy, probably because it is, and doesn't produce the desired result.

How can I set the background (red) of the header of the open panel on page load and how can I toggle classes using js?

/** Accordions JavaScript **/
/** Dynamically add id **/
$.each($(".accordion"), (function (index) {
   $(this).attr("id", "accordion_" + parseInt(index + 1));
}));

/** Dynamically add interaction **/
$.each($(".accordion > .card"), (function (index, value) {
   var num = index + 1;
   $(value).children(".card-header").attr("id", "heading_acc_" + num);
   $(value).find(".card-header > .card-title").wrapInner("<button  class=\"btn btn-link\" type=\"button\" data-toggle=\"collapse\" aria-expanded=\"false\"></button>");
   $(value).find(".card-header > .card-title > button").attr({
      "data-target": "#collapse_acc_" + num,
      "aria-controls": "collapse_acc_" + num
   });
   $(value).children(".collapse").attr({
      id: "collapse_acc_" + num,
      "aria-labelledby": "heading_acc_" + num
   });
}));


$(document).ready(function(){
// Add class highlight to panel with show class
$(".collapse.show").each(function(){
    $(this).prev(".card-header").addClass("highlight");
});

// toggle highlight class
$(".card-header .btn").click(function(){
    $(".card-header").toggleClass("highlight");
});
});
/* ========= Accordions ==========*/
.accordion .card .card-header {
  border-bottom: 1px solid grey
}

/* Accordion Standard */
.accordion:first-child {
  margin-top: 5px;
  border-radius: 0
}

.accordion h1,
.accordion h2,
.accordion h3,
.accordion h4,
.accordion h5,
.accordion h6 {
  font-size: 1.125rem
}

.accordion .card {
  border: 0;
  border-radius: 0;
  padding-top: 0;
  padding-bottom: 0
}

.accordion .card .card-title {
  margin-bottom: 0;
  margin-top: 0;
  padding-top: 0;
  padding-bottom: 0
}

.accordion .card .card-title button {
  color: black
  font-weight: bold;
  font-size: 1.25rem;
  padding: 1.25rem;
  border: 1px solid grey
  border-radius: 0;
  background-color: #FFF;
  padding-left: 70px
}

.accordion .card .card-title button:hover,
.accordion .card .card-title button:focus {
  color: #174EA6
}

.accordion .card .card-title button::after {
  -webkit-font-smoothing: antialiased;
  display: inline-block;
  font-style: normal;
  font-variant: normal;
  text-rendering: auto;
  line-height: 1;
  font-family: "Font Awesome 6 Free";
  font-weight: 900;
  position: absolute;
  top: 1.5rem;
  left: 2rem
}

/* Open and Close Icon */
.accordion .card .card-title button[aria-expanded="true"]:after {
  content: "\f077";
  transform: rotate(0deg);
  transition: .3s all;
  color: black
}

.accordion .card .card-title button[aria-expanded="false"]:after {
  content: "\f054";
  transform: rotate(90deg);
  transition: .3s all;
  color: black
}

.accordion .card .card-header {
  padding: 0
}

.accordion .card .card-header button {
  border: 1px solid grey
  background-color: #fff;
  width: 100%;
  text-align: left
}

.accordion .card-body {
  border: 1px solid grey;
  background-color: white
}

/* Active Accordion Panel - Background and Text Color */
.accordion .card .card-header button[aria-expanded="true"] {
  background-color: red;
  color: #ffffff
}

.accordion .card-header.highlight {
    color: #fff;
    background-color: red;
}
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js"></script>
     <link href=" https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" rel="stylesheet" />
    <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css" rel="stylesheet" />
    
    <h2>Accordion: Group of 2 - First panel open</h2>

       
            <!-- Accordion headings should be changed to respect page hierarchy -->
            <!--start accordion-->
            <div class="accordion mdb-shadow-4 my-5">
               <div class="card">
                  <div class="card-header">
                     <h2 class="card-title">
                        Accordion 1 of 2
                     </h2>
                  </div>
                  <div class="collapse show">
                     <div class="card-body">
                        <p>Loren ipsum
                        </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>
            <!--end accordion-->


Solution

  • You could also simplify a bit. You're already dynamically adding the wrapper on your accordion components. The only thing you're missing is that you're not setting the aria-expanded attribute to true for the ones that have the show property. To do that you can check for the presence of the show class and add the value dynamically as follows:

    
    function addIds(index) {
      $(this).attr("id", "accordion_" + parseInt(index + 1));
    }
    
    function addDataAttrs(index, value) {
      const num = index + 1;
      // check if any child has the show class
      // the length of the array of elements will be 
      // either 0 (falsy) or a number greater than 0 (truthy)
      // use a ternary to store a string value of "true"
      // or "false" in the variable
      const isShowing = $(value).children(".show").length ? "true" : "false";
    
      $(value)
        .children(".card-header")
        .attr("id", "heading_acc_" + num);
      $(value)
        .find(".card-header > .card-title")
        .wrapInner(
          `<button class="btn btn-link" type="button" data-toggle="collapse" aria-expanded="${isShowing}"></button>`
        ); // use the variable to insert the correct value
      $(value)
        .find(".card-header > .card-title > button")
        .attr({
          "data-target": "#collapse_acc_" + num,
          "aria-controls": "collapse_acc_" + num
        });
      $(value)
        .children(".collapse")
        .attr({
          id: "collapse_acc_" + num,
          "aria-labelledby": "heading_acc_" + num
        });
    }
    

    Now there's no need to have the extra code to add the highlight class and thus you don't need it in your CSS either.

    Working example below:

    const $accordions = $(".accordion");
    const $cards = $(".accordion > .card");
    
    $.each($accordions, addIds);
    $.each($cards, addDataAttrs);
    
    function addIds(index) {
      $(this).attr("id", "accordion_" + parseInt(index + 1));
    }
    
    function addDataAttrs(index, value) {
      const num = index + 1;
      const isShowing = $(value).children(".show").length ? "true" : "false";
    
      $(value)
        .children(".card-header")
        .attr("id", "heading_acc_" + num);
      $(value)
        .find(".card-header > .card-title")
        .wrapInner(
          `<button class="btn btn-link" type="button" data-toggle="collapse" aria-expanded="${isShowing}"></button>`
        );
      $(value)
        .find(".card-header > .card-title > button")
        .attr({
          "data-target": "#collapse_acc_" + num,
          "aria-controls": "collapse_acc_" + num
        });
      $(value)
        .children(".collapse")
        .attr({
          id: "collapse_acc_" + num,
          "aria-labelledby": "heading_acc_" + num
        });
    }
    /* ========= Accordions ==========*/
    .accordion .card .card-header {
      border-bottom: 1px solid grey
    }
    
    /* Accordion Standard */
    .accordion:first-child {
      margin-top: 5px;
      border-radius: 0
    }
    
    .accordion h1,
    .accordion h2,
    .accordion h3,
    .accordion h4,
    .accordion h5,
    .accordion h6 {
      font-size: 1.125rem
    }
    
    .accordion .card {
      border: 0;
      border-radius: 0;
      padding-top: 0;
      padding-bottom: 0
    }
    
    .accordion .card .card-title {
      margin-bottom: 0;
      margin-top: 0;
      padding-top: 0;
      padding-bottom: 0
    }
    
    .accordion .card .card-title button {
      color: black;
      font-weight: bold;
      font-size: 1.25rem;
      padding: 1.25rem;
      border: 1px solid grey
      border-radius: 0;
      background-color: #FFF;
      padding-left: 70px;
    }
    
    .accordion .card .card-title button:hover,
    .accordion .card .card-title button:focus {
      color: #174EA6;
    }
    
    .accordion .card .card-title button::after {
      -webkit-font-smoothing: antialiased;
      display: inline-block;
      font-style: normal;
      font-variant: normal;
      text-rendering: auto;
      line-height: 1;
      font-family: "Font Awesome 6 Free";
      font-weight: 900;
      position: absolute;
      top: 1.5rem;
      left: 2rem;
      transition: .3s all;
      color: black;
    }
    
    /* Open and Close Icon */
    .accordion .card .card-title button[aria-expanded="true"]:after {
      content: "\f077";
      transform: rotate(0deg);
    }
    
    .accordion .card .card-title button[aria-expanded="false"]:after {
      content: "\f054";
      transform: rotate(90deg);
    }
    
    .accordion .card .card-header {
      padding: 0;
    }
    
    .accordion .card .card-header button {
    /*   border: 1px solid grey; */
      border-radius: 0;
      background-color: #fff;
      width: 100%;
      text-align: left
    }
    
    .accordion .card-body {
      border: 1px solid grey;
      background-color: white
    }
    
    /* Active Accordion Panel - Background and Text Color */
    .accordion .card .card-header button[aria-expanded="true"] {
      background-color: red;
      color: #ffffff
    }
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    
    <h2>Accordion: Group of 2 - First panel open</h2>
    
           
    <!-- Accordion headings should be changed to respect page hierarchy -->
    <div class="accordion mdb-shadow-4 my-5">
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">
            Accordion 1 of 2
          </h2>
        </div>
        <div class="collapse show">
          <div class="card-body">
            <p>Loren ipsum
            </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>
    
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js"></script>
    <link href=" https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" rel="stylesheet" />