JSFiddle - React, Tailwind, and code Playground
by raviteja gunda
HTML
<nav>
<ul class="menu">
<li><a href="#">Home</a></li>
<li class="has-submenu">
<button aria-haspopup="true" aria-expanded="false">About</button>
<ul class="submenu">
<li><a href="#">Our Team</a></li>
<li class="has-submenu">
<button aria-haspopup="true" aria-expanded="false">Our History</button>
<ul class="submenu">
<li><a href="#">History 1</a></li>
<li><a href="#">History 2</a></li>
<li><a href="#">History 3</a></li>
</ul>
</li>
<li><a href="#">Mission & Values</a></li>
</ul>
</li>
<li><a href="#">Services</a></li>
<li class="has-submenu">
<button aria-haspopup="true" aria-expanded="false">Contact</button>
<ul class="submenu">
<li><a href="#">Email Us</a></li>
<li><a href="#">Visit Us</a></li>
</ul>
</li>
</ul>
</nav>
CSS
body {
font-family: Arial, sans-serif;
}
.menu {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.menu > li {
position: relative;
margin-right: 1em;
}
.menu > li > a,
.menu > li > button {
text-decoration: none;
padding: 1em;
display: block;
color: #333;
background-color: #f0f0f0;
border: none;
cursor: pointer;
text-align: left;
width: 100%;
}
.menu > li > a:focus,
.menu > li > button:focus {
background-color: #ccc;
}
.submenu {
list-style: none;
padding: 0;
margin: 0;
position: absolute;
top: 100%;
left: 0;
display: none;
background-color: #f0f0f0;
}
.submenu li {
position: relative;
}
.submenu li a,
.submenu li button {
text-decoration: none;
padding: 1em;
display: block;
color: #333;
border: none;
cursor: pointer;
}
.submenu li a:focus,
.submenu li button:focus {
background-color: #ccc;
}
.submenu .submenu {
top: 0;
left: 100%;
}
.menu .has-submenu[aria-expanded="true"] > .submenu {
display: block;
}
JavaScript
document.addEventListener('DOMContentLoaded', function () {
const menuItems = document.querySelectorAll('.has-submenu > button');
menuItems.forEach(item => {
item.addEventListener('click', function (event) {
event.preventDefault();
// Close all other submenus that are not in the same hierarchy
menuItems.forEach(otherItem => {
const otherSubmenu = otherItem.closest('.submenu');
const currentSubmenu = item.closest('.submenu');
if (otherItem !== item && (!otherSubmenu || otherSubmenu !== currentSubmenu)) {
otherItem.setAttribute('aria-expanded', 'false');
otherItem.nextElementSibling.style.display = 'none';
}
});
// Toggle the clicked submenu
const isExpanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !isExpanded);
const submenu = this.nextElementSibling;
submenu.style.display = !isExpanded ? 'block' : 'none';
});
item.addEventListener('keydown', function (event) {
const submenuLinks = this.nextElementSibling.querySelectorAll('a, button');
const submenuFirstLink = submenuLinks[0];
const submenuLastLink = submenuLinks[submenuLinks.length - 1];
if (event.key === 'Tab' && this.getAttribute('aria-expanded') === 'true') {
if (event.shiftKey) {
if (document.activeElement === this) {
submenuLastLink.focus();
event.preventDefault();
} else if (document.activeElement === submenuFirstLink) {
this.focus();
event.preventDefault();
}
} else {
if (document.activeElement === this) {
submenuFirstLink.focus();
...