Skip to navigation Skip to main content Skip to footer

How to build an accessible accordion with and without JavaScript

An accordion is a pretty common component to find on a website. The problem however, is that most of them are not accessible. In this article we shall cover 2 methods on how to build an accessible accordion. 1 of which requires no JavaScript all. Don't believe me? Let's dive in.

Method 1: The devil is in the details

The 1st method we shall cover is the easiest and simplest of the 2. This method harnesses' the power of semantic HTML in which we will use the <details> element. This is the core of our accordion component. We shall then provide a <summary> element inside our accordion which will act as the title for the accordion. This element is required to give context to the accordion panel we wish to open or close. You can then add your content, using other HTML elements if you wish, and with that you're done. You have a fully working accordion to declutter your content. How simple was that!

<details>
<summary>Details</summary>
<p>Something small enough to escape casual notice.</p>
</details>

Open the accordion by default

By default, the <details> element loads the accordion in a closed state. If you prefer to have the accordion open by default, all you need to do is simply add the open attribute on the <details> and voila. Not a single bit of JavaScript in sight. Beautiful!

<details open>
<summary>Details</summary>
<p>Something small enough to escape casual notice.</p>
</details>

Accessibility support

The <details> element has very good accessibility support overall. You are able to focus on and interact with the element using just a keyboard and support for screen readers is pretty good all round, with a few bugs here and there. I highly recommend you check out Scott O'Hara's in depth article to learn more about the accessibility support of the <details> element and the current bugs that exist.

Browser support

At the time of writing, when it comes to browser support, the <details> element is supported across all modern browsers according to MDN, which is an absolute win. The only browsers that don't support the element are Internet Explorer (no surprise there) and Opera.

Method 2: Semantic HTML and JavaScript

If you are not in a position to use the native <details> element due to the need to support older browsers or for some other reason, then have no fear. With the use of some semantic HTML and a little bit of JavaScript, we can still make a fully functional and accessible accordion.

The HTML structure

Let's start things off by getting our HTML in place. We shall kick things off by creating a a container to house our accordions should we wish to have more than one. For each accordion we shall use the <button> element, which will contain the title of the accordion and will be the element the user interacts with to open and close the accordion. On the <button> element we shall add the attribute aria-expanded="false". This will tell assistive technologies whether the accordion is open or closed.

Next up, we will add a data attribute to the our accordion container which we will use later in our JavaScript. To add some extra semantics to the mix, we can wrap our <button> element inside a heading element. We should now have something that looks like so:

<div data-accordion>
<h3>
<button type="button" aria-expanded="false">Accordion title</button>
</h3>
</div>

Looking good so far. Next, we shall add a <div> element which will be our accordion panel. If you wish to hide the panel by default we can add the hidden attribute. Under the hood this attribute simply adds the CSS property display: hidden; to the panel thus hiding it. Nice!

<div data-accordion>
<h3>
<button type="button" aria-expanded="false">Accordion title</button>
</h3>

<div hidden>
<p>This is our accordion panel content which will be hidden by default.</p>
</div>
</div>

The JavaScript

Now we have our HTML in place it's time to add the JavaScript functionality that will open and close the accordion. We need to start off by selecting our accordion container and adding a click event listener to the container. We are going to use the bubbling method to determine whether a button has been clicked, rather than selecting all the buttons, looping through each one and adding a click event listener.

const accordionContainer = document.querySelector('[data-accordion]');

accordionContainer?.addEventListener('click', handleClickEvent);

Inside our click event listener we now need to check if the element that was clicked was a <button> element. If it was we then need to change the value of the aria-expanded attribute to be true if we want to open the accordion or false if we want to close it.

const accordionContainer = document.querySelector('[data-accordion]');

function handleClickEvent(event: MouseEvent) {
const target = event.target;

if (target instanceof HTMLButtonElement) {
const isExpanded = target.getAttribute('aria-expanded') === 'true';

target.setAttribute('aria-expanded', `${!isExpanded}`);
}
}

accordionContainer?.addEventListener('click', handleClickEvent);

Finally, we need to select the relevant accordion panel and remove or add the hidden attribute to the panel to determine whether to hide or show it. To do this we shall use the <button> element we clicked and select its parent heading and then select the headings next element sibling which will be the accordion panel.

const accordionContainer = document.querySelector('[data-accordion]');

function handleClickEvent(event: MouseEvent) {
const target = event.target;

if (target instanceof HTMLButtonElement) {
const panel = target.parentNode.nextElementSibling;
const isExpanded = target.getAttribute('aria-expanded') === 'true';

target.setAttribute('aria-expanded', `${!isExpanded}`);

if (isExpanded) {
panel.setAttribute('hidden', '');
} else {
panel.removeAttribute('hidden');
}
}
}

accordionContainer?.addEventListener('click', handleClickEvent);

Voila! We should now have a fully functional and accessible accordion. I have put together a little CodePen of the accordion in action should you wish to see the full code in all its glory.

Summary

With the <details> element having very good browser and accessibility support, We no longer need to rely on JavaScript to make an accessible accordion. All of that is built right into the element. However, for those (now rare) occasions where we can't use this element, we have covered an approach we can take where with a little bit of JavaScript and good use of semantic HTML, we can still make an accessible accordion.