Help with accordion behavior

I am trying to add two functions to this accordion and am having trouble figuring it out.

  1. I would like only one accordion panel to be open at a time.
  2. If an accordion panel is open I would like to be able to close it by clicking on the open accordion tab.

Any help with this would be greatly appreciated. Thanks.

I’ve created a codepen with what I’ve got so far.
https://codepen.io/markmulvey/pen/xYXgZj

I figured it out on my own. Updated pen.

Now to figure out a super simple way to get a circle plus and minus icon to work?

You could try adding a separate class for the vertical plus line (still using the after pseudoelement, so you code still is the same).

Html:
.plus vertical-line

CSS:

.plus.vertical-line:after {
//your code here
}

Then all you need to do is toggle that second class to get a plus or minus as desired.

Hope this helps (this is my first time trying to help someone out on fCC)?

Thanks, Oliver for helping. Honored to be your first. What would the javascript code be to toggle that? That’s where I am struggling.

I have managed to come up with a solution(although its not very pretty).

In order to get the toggling of classes to work, you need to remove the code for .plus:after and place into into the .vertical-line:after class. Then when you remove the .vertical-line from the elements class, it will turn it into a minus symbol.

You can see an example of how to manipulate css classes using the DOM (including toggling) here:
https://www.w3schools.com/jsref/prop_html_classname.asp

Trying to do this for more than one element is the tricky bit, but you already have much of the code that you need. The only bit that is really very complicated is traversing the DOM because it is quite long winded.

You can find a list of the DOM element objects properties here: https://www.w3schools.com/jsref/dom_obj_all.asp. Looking at your code, I assume that you have familiarity with some of them, but the ones that you particularly want to have a look at are: previousElementSibling and lastElementChild. I used one more to get it to work but, hopefully this will give you enough to get started.

Once you have managed to navigate to the element you want (I suggest you see what element you are targeting using console.log.), all you need to do is set the elements class name(s) to get the plus or minus as desired. (HINT: when the panel body height changes, so will the plus or minus sign.)

Hopefully, this has given you enough to go on. I have forked your pen so if you get really stuck I will post a link to something that works. (having worked through it you might want to change plus to minus and vertical-line to plus to make the code function clearer.)

On a side note: some of the div’s in your html code don’t do anything that couldn’t be done on the inner element and this makes DOM traversal harder, so you might want to see what div’s you can get rid of to make things easier.

I hope I have helped (I don’t like posting full solutions unless really necessary, as they are counter productive in helping you learn to code- at least that is what I have found for my own learning), please do let me know if you get stuck and I will do my best to help.

Appreciate all your help with this. I got somewhere but I am missing something to get it back to the original state?

What you need to do is reference the particular plus sign that is associated with the accordion title. You can do that within the code you already have.

if (panel.style.maxHeight){
      //set plus here
      panel.style.maxHeight = null;
    } else {
      
              for(var it of acc) {
              //set plus here
              it.classList.remove("active");
              it.nextElementSibling.style.maxHeight = null; 
    
    }
      //set minus here
      panel.style.maxHeight = panel.scrollHeight + "px";
      
    } 

You will need to set the class to plus when you want the minus to be displayed and to plus vertical-line when you want the plus display. This is a little confusing, hence why you might want to change the class names.

To get the element to change, instead of using an id (which you would have to use a different one for each plus sign), you want to use the it element of your for loop as a base for navigation so:

it.lastElementChild.//other elements here

Try using console.log(it.lastElementChild) if you haven’t already as you will be able to see where you are at and where you need to target next i.e a lower child, sibling or parent.

I hope this make more sense?

Unfortunately no. Would show me the working example you did and I’ll deconstruct from there. Again appreciate your help.

Sure, the JS code you need is:

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

for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var panel = this.nextElementSibling;  

    
    if (panel.style.maxHeight){
      panel.previousElementSibling.lastElementChild.lastElementChild.firstElementChild.className = "plus vertical-line";
      panel.style.maxHeight = null;
    } else {
              for(var it of acc) {
                it.lastElementChild.lastElementChild.firstElementChild.className = "plus vertical-line";
        it.classList.remove("active");
        it.nextElementSibling.style.maxHeight = null;
        
    
        }
      panel.previousElementSibling.lastElementChild.lastElementChild.firstElementChild.className = "plus";
      panel.style.maxHeight = panel.scrollHeight + "px";
    } 
  });
}

If you copy that into you latest pen, then the accordion should work nicely. As I said it is not pretty, but it works. If you want an easier solution out of the box, then you might want to check out the bootstrap library which has accordion functionality built in.

Hope this helps :slightly_smiling_face:

Brilliant! Thank you so much and I see what you mean about traversing the DOM. Going to work on trying to clean that up and simplify, but now I see the light.

Cheers!