Popover Api for menu
by Julien Roche
HTML
<div class="menu">
<button popovertarget="mainpopover">Menu</button>
<div id="mainpopover" popover="auto" class="menu__mainpopover">
<nav class="menu__listcontainer">
<a href="#">Home</a>
<div class="menu__subcontainer">
<a href="#" tabindex="0">Pizza</a>
<div id="subpopover" popover="auto">
<div class="menu__listcontainer">
<a href="#">Margherita</a>
<a href="#">Pepperoni</a>
<a href="#">Ham & Shroom</a>
<a href="#">Vegan</a>
</div>
</div>
</div>
<a href="#">Music</a>
<a href="#">Wombats</a>
<a href="#">Finland</a>
<div class="menu__subcontainer">
<a href="#" tabindex="0">Pizza</a>
<div id="subpopover" popover="auto">
<div class="menu__listcontainer">
<a href="#">Margherita</a>
<a href="#">Pepperoni</a>
<a href="#">Ham & Shroom</a>
<a href="#">Vegan</a>
</div>
</div>
</div>
</nav>
</div>
</div>
CSS
body,
html {
font-family: sans-serif;
}
.menu {
display: inline-block;
button {
anchor-name: --menu;
}
.menu__mainpopover {
border: 1px solid black;
width: 120px;
inset: unset;
top: calc(anchor(self-end) + 0.25rem); /* === anchor(bottom) */
left: anchor(self-start); /* === anchor(left) */
position-anchor: --menu;
& .menu__listcontainer {
display: flex;
flex-direction: column;
}
.menu__subcontainer {
display: flex;
flex-direction: column;
&.menu__subcontainer--active {
anchor-name: --submenu;
}
& > a {
position: relative;
&::after {
content: '➤';
font-weight: bold;
position: absolute;
top: 0;
right: 0.5rem;
}
}
& [popover] {
border: 1px solid black;
width: 120px;
inset: unset;
top: anchor(self-start); /* === anchor(top) */
left: calc(anchor(self-end) - 1rem); /* === anchor(right) */
position-anchor: --submenu;
}
}
a {
flex: 1;
text-decoration: none;
outline: none;
text-align: center;
line-height: 3;
color: black;
&:link,
&:visited {
background: palegoldenrod;
color: black;
}
&:hover,
&:focus {
background: orange;
}
&:active {
background: darkred;
color: white;
}
}
}
}
JavaScript
const mainpopover = document.querySelector('.menu__mainpopover');
const subcontainers = Array.from(document.querySelectorAll('.menu__subcontainer'));
subcontainers.forEach((subcontainer) => {
const subpopover = subcontainer.querySelector('[popover]');
[
'pointerover', // Event to show the subpopover when the mouse moves over
'focusin', // Event to make the subpopover keyboard-accessible
].forEach((eventName) => {
subcontainer.addEventListener(eventName, (event) => {
subcontainer.classList.add('menu__subcontainer--active');
event.stopPropagation();
subpopover.showPopover();
});
});
// Event to hide the subpopover when the mouse moves out
subcontainer.addEventListener('pointerout', () => {
subcontainer.classList.remove('menu__subcontainer--active');
subpopover.hidePopover();
});
});
// Events to hide the popover menus when an option is selected in either of them
mainpopover.addEventListener('pointerdown', () => {
subcontainers.forEach((subcontainer) => {
const subpopover = subcontainer.querySelector('[popover]');
subcontainer.classList.remove('menu__subcontainer--active');
subpopover.hidePopover();
});
mainpopover.hidePopover();
});
mainpopover.addEventListener('focusin', () => {
subcontainers.forEach((subcontainer) => {
const subpopover = subcontainer.querySelector('[popover]');
subcontainer.classList.remove('menu__subcontainer--active');
subpopover.hidePopover();
});
});