Automatically close other <details>

This post is about closing all <details> tag when another one is open.

I was writing a post about MidDay, my new WordPress Theme, and wanted to include a simple FAQ section but with a little of style, this is when I remembered the <details> tag.

It's an awesome accessible element, similar to an accordion.

How to create one ? You have just opened one 😁
Just follow the pattern in the code.
<details>
<summary>Summary/Question about the content</summary>
And here is the content, it could be anything,
even code like this example 😉
</details>

But when we use more than one <details> tag on the same page, especially when they are one after another, we can see that if one is opened and we open another one, the first stays opened.
Imagine having 5 or more after each other, with a reasonable amount of content, they will take a lot of space.
As an example, you can take a look at this informative pen : Teaching the Details Element with ... Details Elements!

So how to close the opened one ? Easy !
Just include the following code in your scripts file or in a script tag at the bottom of your page before the closing body tag.
/** Close others <details> tag */
const allDetails = document.querySelectorAll('details');

allDetails.forEach(details => {
details.addEventListener('toggle', (e) => {
if (details.open) {
allDetails.forEach(details => {
if (details != e.target && details.open) {
details.open = false;
}
});
}
});
});

Let's break it down !

First we select all the <details> tag :

const allDetails = document.querySelectorAll('details');

And we iterate over them :

allDetails.forEach(details => {

Then we listen to the toggle event used by the <details> element whenever its state changes between open and closed :

details.addEventListener('toggle', (e) => {

Now, we wait for any <details> to be open :

if (details.open) {

We iterate once again over all the <details> and check if anyone of them was not the target of the toggle event and was open, if so, we just close it by removing it's open state :

allDetails.forEach(details => {
if (details != e.target && details.open) {
details.open = false;
}
});

In other words, if a <details> element was opened, it have the open attribute. Now if another <details> element is open, we remove the open attribute from the previous, leaving only the actual targeted one opened.

Hope that this was useful, and that you'll start using The Details disclosure element.

SYA,
LebCit.