JavaScript Accordion Menu

Accordions are everywhere on the web and even in mobile apps. They’re a great way to show and hide content based off user interaction. In this guide, I’ll go over how to create an accordion using HTML, CSS and a little bit of JavaScript. If you’re interested in seeing how to create one with only HTML, check out this blog article where I go over that:
HTML-Only Accordion

If you have a basic understanding of HTML, CSS and JavaScript, let’s get started!

Also, If you want to follow along, this will be a step-by-step guide. So be sure to setup a new project folder or github repo. As always, we’ll start by creating some of the files we’ll need. I’ll create an index.html file, styles.css stylesheet, & an app.js file for our JavaScript.

HTML

In the index.html file, i’ll get it started with some boiler-plate code and add in our stylesheet and JavaScript file.

<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="./styles.css"> <title>Document</title>
</head>
<body> <script src="./app.js"></script>
</body>
</html>

Now that that is all setup, let’s write up the markup for our accordions. We can start by setting up a container for all of our accordions.

<div class="accordion-container">
</div>

Each accordion we create will live inside of this container. Let’s set up one now.

We can create an accordion by using a div with the class of accordion. Each accordion will have 2 children elements; accordion-header and accordion-body. This is what that markup will look like:

<div class="accordion"> <div class="accordion-header"></div> <div class="accordion-body"></div>
</div>

The accordion-header will be the part of our accordion that is always visible to the end-user. This will display the accordion’s title as well as a button. So let’s add that in now. We can set up a title by using an h2 element and we’ll set up a button using the button tag.

<div class="accordion-header"> <h2>Accordion Title</h2> <button> + </button>
</div>

For this guide, we’ll just have the accordion title say Accordion Title and for the button’s text content, we’ll just use a + sign.

As for the accordion-body, this will just hold the content that will not be visible to the end-user until they click on the accordion. This could be anything like images, lists, more accordions, etc. For this example, we’ll just add in a paragraph tag.

<div class="accordion-body"> <p>Hello World!</p>
</div>

That should be all we need for our HTML. Let’s work on the styles next.

CSS

As always, I’ll start off my stylesheet with a small reset.

* { margin: 0; padding: 0; box-sizing: border-box;
}

The only styles I’ll apply to our accordion-container div is just some padding.

.accordion-container { padding: 3rem;
}

Next, let’s start styling the accordion and it’s children. The accordion itself will get a light grey background-color.

.accordion { background-color: #eee;
}

We’ll first get started with the accordion-header and accordion-body by adding some padding to both:

.accordion-header, .accordion-body { padding: 1rem;
}

Now let’s work on our accordion-header.

We’ll want the title to appear on the left and the button to appear on the right. This is the perfect use-case for something like flex-box. So let’s use it. We’ll set the display property to flex and we’ll justify the content with space-between since we want space between our two elements (h2 and button). We’ll then align the items to the center. We’ll add a slightly darker gray background color. We can also add a cursor of pointer to let the user know it can be clicked. We’ll want to add a background color when the user hovers over it as well so let’s add a transition for that background color of .3s. This is how your styles should look:

.accordion-header { display: flex; justify-content: space-between; align-items: center; background-color: #ccc; cursor: pointer; transition: background-color .3s;
}
.accordion-header:hover { background: #aaa;
}

Let’s add a little bit of styles to the button now.

We can give it a border of none, set the background color to transparent and up the font-size to 1.5rem.

.accordion-header button { border: none; background: transparent; font-size: 1.5rem;
}

Great, now let’s finish up the styles by working on the accordion-body. This is the part of the accordion that is not visible to the user by default. The user will need to click the accordion to show this content. So let’s set the display to none.

.accordion-body { display: none;
}

We will add a class of active to the accordion when it is clicked. So we can toggle that display property accordingly:

.accordion.active .accordion-body { display: block;
}

Basically, we are targeting the .accordion rule when it has a class of active. .accordion.active and then targeting the accordion-body and setting it’s display to block.

Now that we have all of our styles in place, let’s go back into the HTML and add in one or two more accordions. Our finished HTML and CSS should now look like this:

HTML:

<div class="accordion-container" id="accordionContainer"> <div class="accordion"> <div class="accordion-header"> <h2>Accordion title</h2> <button>+</button> </div> <div class="accordion-body"> <p>Hello World!</p> </div> </div> <div class="accordion"> <div class="accordion-header"> <h2>Accordion title</h2> <button>+</button> </div> <div class="accordion-body"> <p>Hello World x2!</p> </div> </div>
</div>

CSS:

* { margin: 0; padding: 0; box-sizing: border-box;
} .accordion-container { padding: 3rem;
} .accordion { background-color: #eee;
}
.accordion-header { display: flex; justify-content: space-between; align-items: center; background-color: #ccc; cursor: pointer; transition: background-color .3s;
}
.accordion-header:hover { background: #aaa;
}
.accordion-header button { border: none; background: transparent; font-size: 1.5rem;
}
.accordion-header, .accordion-body { padding: 1rem;
}
.accordion-body { display: none;
} .accordion.active .accordion-body { display: block;
}

The only thing left now is to toggle this active class via JavaScript.

JavaScript

Let’s first grab every accordion on our page and set it up in a variable.

const accordions = document.querySelectorAll('.accordion');

Now, we can run a loop on each instance of our accordion and add an event listener to each one.

accordions.forEach(accordion => { accordion.addEventListener('click', e => { })
});

What do we want to do? Well, we want to grab the clicked accordion and toggle the class of active on it. Since we set up the variable of accordion for each instance of our accordions in the forEach loop, we can just write:

accordion.classList.toggle('active');

Hitting save, we should now see the accordion ‘expand’ when clicked. It should also close when clicked again. This should work for each accordion on our page.

Great work! You just set up some accordions with basic HTML, CSS, and some JavaScript. Though setting up an accordion HTML only works just as fine as what we created here, you have many more options for your accordion when you write your own code. Like for example, we could change the button from a + sign to a sign when opened/closed. Why don’t we do that now?

Inside the same eventListener we were just working with, let’s add an if statement.
What do we want to test? Well, we want to test that if the accordion we clicked’s button textContent is equal to a + sign, we want to change it to a sign and do the opposite if it has a sign. This is really easy. Lets first grab the button. We can do that by writing:

let accordionBtn = accordion.querySelector('button');

Now we just need to write our if statement.

if (accordionBtn.textContent === '+') { accordionBtn.textContent = '-';
} else { accordionBtn.textContent = '+';
}

The full JavaScript should now look like this:

const accordions = document.querySelectorAll('.accordion');
accordions.forEach(accordion => { accordion.addEventListener('click', e => { accordion.classList.toggle('active'); let accordionBtn = accordion.querySelector('button'); if (accordionBtn.textContent === '+') { accordionBtn.textContent = '-'; } else { accordionBtn.textContent = '+'; } })
});

If everything was written correctly, you should now have a few accordion menus on the page that can be opened and closed. The respective accordion button should also change with the open/closed state of the accordion. Great work!