I've seen some people battle with accordions in the past and, well, it wasn't pretty. BUT... It's completely understandable! An accordion has a whole lot going on and on a first sight it seems like all the functionalities will be pretty hard to develop.
- selecting elements on the DOM
- forEach loops
- event listeners
- toggling class lists
If you're really just interested on the code, here's a link to the CodePen with it. I also have a YouTube video for it if you're more of a visual person 👇
So, with all of that out of the way, let's start this post 😎
Starting with the HTML
We'll start by create a basic structure of the HTML. And here it's pretty straight forward. You'll want a
wrapping <div> that will hold your accordion component and inside it you'll have different accordion items. Inside each item you'll want to have two things:
- the content that will always show (it can be just a
<p>or it can be an entire
<div>that will have the content that will collapse (AKA, that will appear and disappear when you press 1).
A made a image to illustrate the basic structure and I advise you, specially when you're starting, to sketch out what you're intending to build since it makes it easier to split everything into smaller parts so you can work on them separately.
So, now that we have our structure, we can build it. In my example below, as you can see I have the mentioned
<div> that has everything inside and that's our accordion component and inside each I have an
accordionTitle, which represents the content that will always be showing, and a
accordionContent that will be the content that will appear and disappear. You can change up the HTML elements that you use (maybe you would prefer to use
<li>) but that's on you! And that's our HTML 🎉
A step further with our CSS
We have our HTML up and that's great but that's not an accordion. We have at least to hide
accordionContent to at least make it look like one so that's what we're going to do. We simply want to hide that content on this step so what we're going to add to our CSS is the following.
The styling for the
.accordionTitle it's just a matter of preference. I noticed, when clicking on
.accordionTitle that I was getting highlighted text and I didn't wanted that so I chose to remove it with user-select and since I wanted for the user to know that this element was clickable, I changed the cursor that appears when you over it to a pointer. That's it.
.accordionTitle + .accordionContent is what matters and, honestly, the adjacent sibling combinator is pretty much all you want here. It will style your
.accordionContent based on if it immediately follows
.accordionTitle and, on for my accordion structure, it's just what I need.
So, we've hidden our content but now we want to show it when we click on
accordionTitle (or show it if it's showing, of course). So we want to grab this
accordionTitle class and add an event listener to it, in this case a click event, and then some magic will end happening!
.accordionTitle and we'll do it with querySelectorAll().
This piece of codes grabs all the elements that have this class name and returns a
NodeList is an object that has a collection of
nodes in it which, in this case, it's our elements that have the
.accordionTitle in them, which means, our
Now that we have our elements, we need to add to each of them a click event and for that we'll use a forEach loop.
forEach loop allows us to go through each element of an array (or, in this case, the NodeList) and do something to it. It's very similar to a
.map() but, unlike
.map(), it will not return anything from it because any
return inside a
forEach will be discarded. I'm using the
forEach because I want to use the original data from my array/nodeList and do something with it instead of changing it.
So, we'll do something like the following.
Now, inside these brackets we can define what we're doing with each item, our
accordionTitle, and we know that we want for something to happen when we click on them so we'll add an
eventListener to them.
Here's we saying that, when we click on an
accordionTitle something will happen and we'll define what happens inside these new brackets.
So... We know that now our
div with the content is hidden and we want to show it so... how can we do that? Here's my approach to it:
On our CSS we're currently hiding the content based on our
.accordionTitle element and I want to keep that logic to show it as well. Which means that I want to alter our
.accordionTitle in some way that it allow for our
.accordionContent to have a different styling (throwback to the adjacent sibling combinator).
classList of our
accordionTitle by adding (or removing) a new class called
So, what's my way of thinking here?
If I can add a class to my
accordionTitle, and I'm controlling this accordion content
div with that same element on my CSS, I can add a new CSS rule which tells my code that, when the
.accordionTitle also has the class
is-open, then the
.accordionContent that comes immediately after it should have a
display: block and it looks like this.
So, once again, I'm controlling
.accordionContent visibility, or presence, with
.accordionTitle and by toggling a new class to
.accordionTitle, I'm able to show and hide
.accordionContent as I wish.
Let's go even further
Currently our code allows us to open and close any tab but all the others that might be open stay like that and that's not really the perfect accordion so let's work on it, shall we? Currently we're toggling each element independently but that's not what we want. We want to check if there are already any element that are open already and we want to remove that property so this is what I'm going to do:
I'll start by removing our
toggle and first I want to create an if/else statement. On my
if I want to check if the
accordionTitle has the class
else will be responsible for adding the
is-open class and we can do it like this
At this point we're basically at the same level that we were with the
toggle. Now, on this
else statement I want to see if there are any other elements with the
.is-open class and, if there are, I want to remove it and we can do it like this.
First we do a
querySelectorAll for all the elements with the
.is-open class like this
Then we'll need to run a new
forEach loop to iterate over each element so we can remove the class and that looks something like this
And we're done! Now once you click on a tab the other will close and we have a fully functioning accordion! 🎉🕺 Here's the working code 👇
Here's a challenge for you
I would like to challenge you to do something now: using what you've learned so far, I would like for you to create a button that would close and open all the tabs. Are you up for the challenge? If you are, send me your code to my Twitter 😄
And that's it!