An accordion, in development and design, is a graphical control element that consists of vertically stacked headers and hidden internal content. When clicked, a header's previously collapsed/hidden content box will expand to show its content; often text, images, or other grouped information.
You've probably seen (or used) an accordion on a FAQ page, with the questions shown in the headers, and the answers to those questions hidden in the content boxes.
Accordions can help increase the user experience on web and application pages with lots of information. They allow developers to group all that information on one page, but only display the higher level headers/titles. Users can then glance over all the titles without being overwhelmed by the details. They can more easily find, and click on, the headers/titles that they are interested in, and access the greater detail of the content.
There are countless widgets, plugins and other code snippets that will auto-magically add an accordion to your website or app. But you can also build a simple accordion with only HTML, CSS and JavaScript.
Accordion HTML
<ul id="accordion">
<li>
<button aria-controls="content-1" aria-expanded="false" id="accordion-control-1">FAQ 1</button>
<div class="acc-item-content" aria-hidden="true" id="content-1">
<p>Answer 1!</p>
</div>
</li>
<li>
<button aria-controls="content-2" aria-expanded="false" id="accordion-control-2">FAQ 2</button>
<div class="acc-item-content" aria-hidden="true" id="content-2">
<p>Answer 2</p>
</div>
</li>
<li>
<button aria-controls="content-3" aria-expanded="false" id="accordion-control-3">FAQ 3</button>
<div class="acc-item-content" aria-hidden="true" id="content-3">
<p>Answer 3</p>
</div>
</li>
<li>
<button aria-controls="content-4" aria-expanded="false" id="accordion-control-4">FAQ 4 </button>
<div class="acc-item-content" aria-hidden="true" id="content-4">
<p>Answer 4</p>
</div>
</li>
<li>
<button aria-controls="content-5" aria-expanded="false" id="accordion-control-5">FAQ 5</button>
<div class="acc-item-content" aria-hidden="true" id="content-5">
<p>Answer 5</p>
</div>
</li>
</ul>
For the HTML, our entire accordion is housed in an unordered list. Each list item holds a div with the inner content and a button that will toggle the div's visibility. In an effort to make the accordion more accessible, we have aria-expanded
and aria-hidden
attributes, as well as aria-controls
attributes on the buttons that correspond with the ids of the acc-item-content
divs. These attributes will help users using screen readers understand our accordion, and what is and is not visible when the buttons are clicked on.
I've also got my text in paragraph tags, which will be helpful if you have more than a few sentences in the content divs.
Hopefully you're using a loop somewhere to dynamically create each list item and its child elements.
Accordion CSS
ul {
list-style: none;
}
#accordion button:focus {
border-radius: 0px;
outline: none;
}
#accordion button {
outline: none;
background-color: DarkSeaGreen;
padding: 10px;
border: none;
border-bottom: 1px solid darkslategrey;
color: white;
width: 100%;
text-align: left;
font-size: 16px;
border-radius: 0px;
}
#accordion li {
border: 1px solid DarkSlateGray;
border-bottom: none;
}
.acc-item:last-child {
border-bottom: 1px solid DarkSlateGray;
}
#accordion button::after {
content: "\002B";
font-weight: 900;
font-size: 22px;
float: right;
}
#accordion {
width: 80%;
max-width: 800px;
min-width: 275px;
margin: auto;
}
Most of the CSS is for...style. We add background colors, borders and pseudo content to visually indicate that this is an accordion, and that you should click if you want to see more.
Technically, the only rule set you need is this one:
.acc-item-content {
padding: 0px 10px;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
It sets the height of the content divs to 0 (hiding them from view); and gives the max-height a transition style and speed. This will come in handy when we get to the JavaScript, where we'll change the max-height values for our divs when the buttons are clicked.
Accordion JavaScript
window.addEventListener("DOMContentLoaded", (event) => {
let buttons = document.querySelectorAll("#accordion button");
buttons.forEach((button) => {
let content = button.nextElementSibling;
button.addEventListener("click", (event) => {
if (button.classList.contains("active")) {
button.classList.remove("active");
button.setAttribute("aria-expanded", false);
content.style.maxHeight = null;
content.setAttribute("aria-hidden", true);
} else {
button.classList.add("active");
button.setAttribute("aria-expanded", true);
content.style.maxHeight = content.scrollHeight + "px";
content.setAttribute("aria-hidden", false);
}
});
});
});
In pseudo code:
When all the DOM content is loaded...
Collect all the buttons that are child elements of the element
with the id #accordion...
Loop through each of these buttons...
Grab the button's sibling element and save it in a variable
called content AND
Add an event listener to each button, so that when the
button is clicked...
If the button has the class active...
Remove "active" from its class list AND
Set its aria-expanded attribute to false AND
Set the content variable's max-height value to null AND
Set the content variable's aria-hidden attribute to true.
Otherwise, if the button doesn't have the class active...
Add "active" to its class list AND
Set its aria-expanded attribute to true AND
Set the content variable's max-height value even
to the value of the content variable's scroll height
(the height of an element's content) AND
Set the content variable's aria-hidden attribute to false.
And that's it: an accessible, simple accordion made with only HTML, CSS and vanilla JavaScript!
Top comments (3)
@lizlaffitte
I'll be using this code for a real world project. Thank you!
Also I included an enhancement that resizes the height of an open accordion item (which uses has a
max-height
value set via JS) to account for resizing the browser width. See my example Pen where I adapt your code (specifically the FAQ 1 tab when open and resizing the browser window width):codepen.io/hollyw00d/pen/zYJEZQr
Just wanted to stop and say thanks for posting this! Really helped with a project I'm working on.
Thank you!