I have a menu that uses buttons and vanilla Javascript.
When a button is clicked, it adds the class opened
to the button and changes aria-expanded="false"
to aria-expanded="true"
.
If you click another button, the original button's attribute reverts to aria-expanded="false"
and the class opened
is removed.
However, if you click the original button a second, consecutive time, whilst the class opened
is removed, aria-expanded="true"
does not change back to aria-expanded="false"
and I am really struggling to understand why.
Here is the script and a mini-menu with minimum CSS (obviously, the browser's inspector is required to view the changes). NB The original JS below was written for a multi-level menu, obviously not used in this instance.
for (const selector of [".btn-level-1", ".btn-level-2", ".btn-level-3", ".btn-level-4", ".btn-level-5", ".btn-level-6",]) {
const buttons = [...document.querySelectorAll(selector)];
for (const button of buttons) {
button.addEventListener('click', () => {
buttons.filter(b => b !== button).forEach(b => {
b.classList.remove('opened'); // this works on self and also if other button is clicked
b.setAttribute("aria-expanded", "false"); //this doesn't work on self
});
button.classList.toggle('opened');
button.setAttribute("aria-expanded", "true");
})
}}
button + ul {
display: none;
}
button.opened + ul {
display: block;
}
li {
padding: 10px;
list-style: none;
}
.sidenav {
width: 560px;
background: #eee;
}
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute;
height: 1px;
width: 0;
overflow: hidden;
white-space: nowrap;
}
.menu-toggle {
height: 44px;
width: 44px;
display: inline-block;
line-height: 44px;
font-size: 25px;
text-align: center;
text-decoration: none;
margin: 0;
}
.icon {
padding-left: 1rem;
}
<nav aria-label="Main Navigation">
<ul>
<li class="has-submenu">
<a href="https://example.com/some-category">Some Category</a>
<button class="btn-level-1 menu-toggle" type="button" aria-haspopup="true" aria-expanded="false">
<span class="christmas-red"></span>
<span class="icon">+</span>
<span class="visually-hidden">
<span class="toggleText">show submenu</span> for "Some Category"</span>
</button>
<ul>
<li><a href="https://example.com/some-category/item-1">Some Category Item 1</a></li>
<li><a href="https://example.com/some-category/item-2">Some Category Item 2</a></li>
</ul>
</li>
<li class="has-submenu">
<a href="https://example.com/some-category">Another Category</a>
<button class="btn-level-1 menu-toggle" type="button" aria-haspopup="true" aria-expanded="false">
<span class="christmas-red"></span>
<span class="icon">+</span>
<span class="visually-hidden">
<span class="toggleText">show submenu</span> for "Some Category"</span>
</button>
<ul>
<li><a href="https://example.com/another-category/item-1">Some Category Item 1</a></li>
<li><a href="https://example.com/another-category/item-2">Some Category Item 2</a></li>
</ul>
</li>
</ul>
</nav>
After toggling the opened
class on button
you need to check whether you actually added or removed it when setting the aria-expanded
attribute. You can do this using classList.contains
. Updated working example:
for (const selector of [".btn-level-1", ".btn-level-2", ".btn-level-3", ".btn-level-4", ".btn-level-5", ".btn-level-6",]) {
const buttons = [...document.querySelectorAll(selector)];
for (const button of buttons) {
button.addEventListener('click', () => {
buttons.filter(b => b !== button).forEach(b => {
b.classList.remove('opened');
b.setAttribute("aria-expanded", "false");
});
button.classList.toggle('opened');
// check which aria-expanded value we should set below
let ariaExpanded = button.classList.contains('opened').toString();
button.setAttribute("aria-expanded", ariaExpanded);
})
}}
button+ul{
display: none;
}
button.opened+ul{
display: block;
}
li{
padding: 10px;
list-style: none;
}
.sidenav {
width: 560px;
background: #eee;
}
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute;
height: 1px;
width: 0;
overflow: hidden;
white-space: nowrap;
}
.menu-toggle{
height: 44px;
width: 44px;
display: inline-block;
line-height: 44px;
font-size: 25px;
text-align: center;
text-decoration: none;
margin: 0;
}
.icon {
padding-left: 1rem;
}
<nav aria-label="Main Navigation">
<ul>
<li class="has-submenu">
<a href="https://example.com/some-category">Some Category</a>
<button class="btn-level-1 menu-toggle" type="button" aria-haspopup="true" aria-expanded="false">
<span class="christmas-red"></span>
<span class="icon">+</span>
<span class="visually-hidden">
<span class="toggleText">show submenu</span> for "Some Category"</span>
</button>
<ul>
<li><a href="https://example.com/some-category/item-1">Some Category Item 1</a></li>
<li><a href="https://example.com/some-category/item-2">Some Category Item 2</a></li>
</ul>
</li>
<li class="has-submenu">
<a href="https://example.com/some-category">Another Category</a>
<button class="btn-level-1 menu-toggle" type="button" aria-haspopup="true" aria-expanded="false">
<span class="christmas-red"></span>
<span class="icon">+</span>
<span class="visually-hidden">
<span class="toggleText">show submenu</span> for "Some Category"</span>
</button>
<ul>
<li><a href="https://example.com/another-category/item-1">Some Category Item 1</a></li>
<li><a href="https://example.com/another-category/item-2">Some Category Item 2</a></li>
</ul>
</li>
</ul>
</nav>