2

I'm currently developing a more complex accordion. The accordion should have 4 buttons (section 0-3) with individual content (lorem ipsum 0-3). By clicking on "section 0" it should display "lorem ipsum 0". By clicking "section 1" the content "lorem ipsum 0" would be replaced with "lorem ipsum 1".

The final website is going to have an unknown amount of rows, each containing 4 accordions. This means, the script must be kind of dynamic.

My approach was to generate IDs with JS, set this attribute to each panel and then call them back again to make the accordion work. But I'm only able to call all the content (like in the JSFiddle) or just the last one from the loop.

How can I display/replace the content for every accordion without losing the layout?

Maybe theres a better approach to achieve this at all?

HTML:

<div class="row">

  <div class="three columns">
    <button class="accordion">Section 0</button>
  </div>  

  <div class="three columns">
    <button class="accordion">Section 1</button>
  </div>  

  <div class="three columns">
    <button class="accordion">Section 2</button>
  </div>    

  <div class="three columns">
    <button class="accordion">Section 3</button>
  </div>  

</div>  

<div class="row">
  <div class="twelve columns">
    <div class="panel">
      <p>Lorem ipsum 0...</p>
    </div>
  </div>
  <div class="twelve columns">
    <div class="panel">
      <p>Lorem ipsum 1...</p>
    </div>
  </div>
  <div class="twelve columns">
    <div class="panel">
      <p>Lorem ipsum 2...</p>
    </div>
  </div>
  <div class="twelve columns">
    <div class="panel">
      <p>Lorem ipsum 3...</p>
    </div>
  </div>
</div>

JS:

var acc = document.getElementsByClassName("accordion");
for (var i = 0; i < acc.length; i++) {
    acc[i].onclick = function() {
        this.classList.toggle("active");

        var writeID = document.getElementsByClassName("panel");
        for (var y = 0; y < writeID.length; y++) {
            var newID = "demo-"+y;
            writeID[y].setAttribute("id", newID);

            var panel = document.getElementById(newID);

            if (panel.style.maxHeight){
                panel.style.maxHeight = null;
            } else {
                panel.style.maxHeight = panel.scrollHeight + "px";
            } 
        }
    }
}

I also created a JSFiddle.

Any help is kindly appreciated!

Cheers' Max

EDIT: I would really appreciate any kind of help here. I tried to solve the problem with closures and forEach, but nothing worked out. If I have to clarify anything above, I'm happy to answer any questions.

Here is the current status of the code. It gives me all the contents out, but I would like to get only one for each accordion:

var acc = document.getElementsByClassName("accordion");
for (var i = 0; i < acc.length; i++) {
  acc[i].onclick = function() {
    this.classList.toggle("active");

      for (var i = 0; i < acc.length; i++) {
        var panel = document.getElementById("demo-"+i);
        if (panel.style.maxHeight){
          panel.style.maxHeight = null;
        } else {
          panel.style.maxHeight = panel.scrollHeight + "px";
        }   
      };  
  }
}

Or in other words, I try to achieve this, but in a dynamic way

var acc = document.getElementsByClassName("accordion");

acc[0].onclick = function() {
  this.classList.toggle("active");
  var panel = document.getElementById("demo-0");
  if (panel.style.maxHeight){
    panel.style.maxHeight = null;
  } else {
    panel.style.maxHeight = panel.scrollHeight + "px";
  }    
}

acc[1].onclick = function() {
  this.classList.toggle("active");
  var panel = document.getElementById("demo-1");
  if (panel.style.maxHeight){
    panel.style.maxHeight = null;
  } else {
    panel.style.maxHeight = panel.scrollHeight + "px";
  }    
} 
Max
  • 153
  • 3
  • 11
  • Let me try to understand your problem before giving my approach! Are the content of each button going to be dynamic, I assume it will be, right? The same with the buttons? – funcoding Oct 16 '17 at 14:18
  • Yes, the content is going to be dynamic. It can be a text or image. The buttons will follow a pattern, so they will have fixed names (like in the example). – Max Oct 16 '17 at 14:20
  • I read now a lot about javascript loop enclosure, but it still can't figure it out. Anyone up for some assistance? Thx! – Max Oct 17 '17 at 08:39

1 Answers1

2

You can add a custom attribute to your buttons with the section number (data-section=3) and use that to open the correct panel with the correct data

var data = ["lorem ipsum ipsum", "lorem lorem lorem", "blah blah blah"];
formatData(data);

function formatData(data) {
  data.forEach(function(text, index) {
    var buttonHTML = "<div class='three columns'>" +
      "<button class='accordion' data-section=" + index + ">Section " + index + "</button>" +
      "</div>";

    var accordionHTML = "<div class='twelve columns'>" +
      "<div id='panel" + index + "' class='panel'>" +
      "<p>" + text + "</p>" +
      "</div>" +
      "</div>";
    document.getElementById("buttonRow").insertAdjacentHTML('beforeend', buttonHTML);
    document.getElementById("panelRow").insertAdjacentHTML('beforeend', accordionHTML);
  })
  var acc = document.getElementsByClassName("accordion");
  for (var i = 0; i < acc.length; i++) {
    acc[i].onclick = function() {
      this.classList.toggle("active");
      var sectionNum = this.getAttribute("data-section");
      var currentPanel = document.getElementById("panel" + sectionNum);
      if (currentPanel.style.maxHeight) {
        currentPanel.style.maxHeight = null;
      } else {
        currentPanel.style.maxHeight = currentPanel.scrollHeight + "px";
      }
    }
  }
}
.democlass {
  color: red;
}

#demo-0 {
  color: red;
}

#demo-1 {
  color: blue;
}

#demo-2 {
  color: green;
}

#demo-3 {
  color: magenta;
}


/* Accordion
–––––––––––––––––––––––––––––––––––––––––––––––––– 
https://www.w3schools.com/howto/howto_js_accordion.asp
*/


/* Style the buttons that are used to open and close the accordion panel */

button.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  width: 100%;
  text-align: left;
  border: none;
  outline: none;
  transition: 0.4s;
}


/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */

button.accordion.active,
button.accordion:hover {
  background-color: #ccc;
}


/* Style the accordion panel. Note: hidden by default */

div.panel {
  padding: 0 18px;
  background-color: white;
  /*    display: none;
*/
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
}

.container {
  position: relative;
  width: 100%;
  /*max-width: 1200px;*/
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}

.column,
.columns {
  width: 100%;
  float: left;
  box-sizing: border-box;
}


/* For devices larger than 400px */

@media (min-width: 400px) {
  .container {
    width: 85%;
    padding: 0;
  }
}


/* For devices larger than 550px */

@media (min-width: 550px) {
  .container {
    width: 95%;
  }
  .column,
  .columns {
    margin-left: 4%;
  }
  .column:first-child,
  .columns:first-child {
    margin-left: 0;
  }
  .one.column,
  .one.columns {
    width: 4.66666666667%;
  }
  .two.columns {
    width: 13.3333333333%;
  }
  .three.columns {
    width: 22%;
  }
  .four.columns {
    width: 30.6666666667%;
  }
  .five.columns {
    width: 39.3333333333%;
  }
  .six.columns {
    width: 48%;
  }
  .seven.columns {
    width: 56.6666666667%;
  }
  .eight.columns {
    width: 65.3333333333%;
  }
  .nine.columns {
    width: 74.0%;
  }
  .ten.columns {
    width: 82.6666666667%;
  }
  .eleven.columns {
    width: 91.3333333333%;
  }
  .twelve.columns {
    width: 100%;
    margin-left: 0;
  }
  .one-third.column {
    width: 30.6666666667%;
  }
  .two-thirds.column {
    width: 65.3333333333%;
  }
  .one-half.column {
    width: 48%;
  }
  /* Offsets */
  .offset-by-one.column,
  .offset-by-one.columns {
    margin-left: 8.66666666667%;
  }
  .offset-by-two.column,
  .offset-by-two.columns {
    margin-left: 17.3333333333%;
  }
  .offset-by-three.column,
  .offset-by-three.columns {
    margin-left: 26%;
  }
  .offset-by-four.column,
  .offset-by-four.columns {
    margin-left: 34.6666666667%;
  }
  .offset-by-five.column,
  .offset-by-five.columns {
    margin-left: 43.3333333333%;
  }
  .offset-by-six.column,
  .offset-by-six.columns {
    margin-left: 52%;
  }
  .offset-by-seven.column,
  .offset-by-seven.columns {
    margin-left: 60.6666666667%;
  }
  .offset-by-eight.column,
  .offset-by-eight.columns {
    margin-left: 69.3333333333%;
  }
  .offset-by-nine.column,
  .offset-by-nine.columns {
    margin-left: 78.0%;
  }
  .offset-by-ten.column,
  .offset-by-ten.columns {
    margin-left: 86.6666666667%;
  }
  .offset-by-eleven.column,
  .offset-by-eleven.columns {
    margin-left: 95.3333333333%;
  }
  .offset-by-one-third.column,
  .offset-by-one-third.columns {
    margin-left: 34.6666666667%;
  }
  .offset-by-two-thirds.column,
  .offset-by-two-thirds.columns {
    margin-left: 69.3333333333%;
  }
  .offset-by-one-half.column,
  .offset-by-one-half.columns {
    margin-left: 52%;
  }
}
<div class="container">

  <div id="buttonRow" class="row">

  </div>

  <div id="panelRow" class="row">

  </div>

</div>
<!-- /container  -->
Andrew Lohr
  • 5,380
  • 1
  • 26
  • 38
  • thank you for your thoughts on the subject. The content (lorem ipsum) needs to be dynamic and fetched from the cms, so I guess this won't work out. Thank you anyway! – Max Oct 17 '17 at 08:40
  • Max I updated the answer to be more dynamic with your data. I put your data as an array of strings, but you could mold it to be another type of object – Andrew Lohr Oct 17 '17 at 13:32
  • Thank you Andrew! I ended up doing it with jQuery – a friend help my out with some code. Thank you for your time & effort – Max Oct 18 '17 at 15:13