Dropdown toggle

By: on May 18, 2024

Dropdown toggles help make interfaces cleaner and more accessible. By hiding options that are not immediately necessary, they prevent information overload and streamline user workflows. This is especially important for side bars and responsive design, where space is at a premium.

Uses of dropdown toggles

  • Forms: Collect information in a structured format while ensuring data consistency.
  • Filtering and Sorting: Enable users to refine and sort data dynamically.
  • Navigation: Often used in responsive designs to accommodate complex menus in limited space.

The toggle effect

In JavaScript, "toggle" generally refers to switching a value or state back and forth between two options. We typically add a display: none; directly to the HTML content we are toggling. Then with the JavaScript click event, we can change the class to display: block; to show the content.

Example

The HTML

To adhere to semantic HTML best practices, use the button tag for the dropdown toggle and the nav tag containing a ul and li tags for the navigation content. If your dropdown content does not use navigation links, you can use a div tag instead.

To make the dropdown more accessible, we can add the ARIA attributes to indicate the expandable nature of the button.

<button class="toggle-button btn-styles" aria-expanded="false" aria-controls="dropdown-nav">
<span class="button-label">Dropdown Toggle</span>
<span class="toggle-icon d-flex">
<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg">
<path d="M23 10L15 18 7 10" stroke="#242424" fill="none" stroke-width="2"/>
</svg>
</span>
</button>
<nav class="toggle-content" id="dropdown-nav" style="display: none;">
<ul>
<li><a href="#">Topic link 1</a></li>
<li><a href="#">Topic link 2</a></li>
<li><a href="#">Topic link 3</a></li>
<li><a href="#">Topic link 4</a></li>
<li><a href="#">Topic link 5</a></li>
</ul>
</nav>

The CSS

To normalize the button styles, I used a set of properties that override the default browser styles. I have a post on Customizing default browser button styles that goes over this in more detail.

For the button text and chevron positioning, we can use display: flex; with justify-content: space-between;.

To change the chevron from down to up we can use transform: rotate(180deg); that will apply when the JavaScript adds the .closed class on click.

<style>
.toggle-button
{
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
background: transparent;
border: none;
padding: 0;
margin: 0;
line-height: normal;
color: inherit;
font: inherit;
cursor: pointer;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.toggle-icon svg.closed {
transform: rotate(180deg); /* Rotates the icon 180 degrees */
}
.toggle-icon svg path {
stroke: var(--clr-primary);
}
nav.toggle-content {
margin-top: 16px;
}
</style>

The JavaScript

First we add the event listener to ensure the code inside the function only runs after the entire HTML Document has been fully loaded and parsed.

Then we select the:

  • .toggle-button when clicked will run the toggleContentVisibility function.
  • .toggle-icon svg to rotate the SVG icon appearance.
  • .toggle-content to show and hide the content.
<script>
document.addEventListener("DOMContentLoaded", function () {
const toggleButton = document.querySelector(".toggle-button");
const toggleIcon = toggleButton.querySelector(".toggle-icon svg");
const toggleContent = document.querySelector(".toggle-content");
function toggleContentVisibility() {
const isCurrentlyHidden = toggleContent.style.display === "none";
toggleContent.style.display = isCurrentlyHidden ? "block" : "none";
toggleIcon.classList.toggle('closed', isCurrentlyHidden);
toggleButton.setAttribute('aria-expanded', isCurrentlyHidden);
}
toggleButton.addEventListener("click", toggleContentVisibility);
});
</script>

isCurrentlyHidden will be a boolean (true or false) depending on whether the element's display property is none. Now we can use this as a flag to determine whether to add the block property and the closed class.

To simplify the code I used a ternary operator here instead of the traditional if-else statement. This works good for less complex functions and allows you to write a conditional assignment in a single line.

Resources