Skip to main content Accessibility Feedback

Only allowing one open dropdown at a time with the details element

Yesterday, we looked at how to use the <details> element to create JavaScript-free dropdown menus.

With that setup, each dropdown acts independently of the other. There’s a good chance that when one dropdown opens, you want any open dropdowns to close so that they don’t overlap with each other.

<nav>
	<ul class="my-nav">
		<li>
			<details class="dropdown">
				<summary>This has dropdown items</summary>
				<ul>
					<li><a href="#hi">Hi</a></li>
					<li><a href="#universe">Universe</a></li>
				</ul>
			</details>
		</li>
		<li>
			<details class="dropdown">
				<summary>Another dropdown</summary>
				<ul>
					<li><a href="#goodbye">Goodbye</a></li>
					<li><a href="#universe">Universe</a></li>
				</ul>
			</details>
		</li>
	</ul>
</nav>

See how these two dropdowns overlap each other?

Today, let’s look at a super tiny bit of JavaScript you can use to close any open dropdowns when opening a new one.

Quick Aside

If you’re going to write JavaScript anyways, why not write your own custom dropdown menu instead?

A few reasons:

  1. If our JavaScript fails, this native dropdown menu will still work, just with reduced functionality.
  2. In browsers that don’t support the <details> element, users can still access the content.
  3. The native element handles accessibility concerns like keyboard interactions and focus management for you.
  4. It’s a lot less JavaScript than you’d have to write otherwise. Take the win!

Listing for toggle events

First, we’ll listen for a special event called toggle on the .my-nav navigation. This event fires on a <details> element when it’s opened or closed.

This event doesn’t bubble, so you’ll need to set useCapture to true.

var nav = document.querySelector('.my-nav');
nav.addEventListener('toggle', function (event) {
	// Do stuff...
}, true);

If the clicked element isn’t open, we’ll do nothing.

Otherwise, we’ll get all of the open dropdowns in our nav element, and close them by removing the [open] attribute. We’ll check that the item isn’t the one we just opened first, though.

var nav = document.querySelector('.my-nav');
nav.addEventListener('toggle', function (event) {

	// Only run if the dropdown is open
	if (!event.target.open) return;

	// Get all other open dropdowns and close them
	var dropdowns = nav.querySelectorAll('.dropdown[open]');
	Array.prototype.forEach.call(dropdowns, function (dropdown) {
		if (dropdown === event.target) return;
		dropdown.removeAttribute('open');
	});

}, true);

And that’s it! Here’s the code in action.