Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.
Dropdown menu with details summary#
I added dropdown menus to Datasette 0.51 - see #1064.
I implemented them using the HTML <details><summary> element. The HTML looked like this:
1<details class="nav-menu">2 <summary><svg aria-labelledby="nav-menu-svg-title" role="img"3 fill="currentColor" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"4 viewBox="0 0 16 16" width="16" height="16">5 <title id="nav-menu-svg-title">Menu</title>6 <path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path>7 </svg></summary>8 <div class="nav-menu-inner">9 <ul>10 <li><a href="/">Item one</a></li>11 <li><a href="/">Item two</a></li>12 <li><a href="/">Item three</a></li>13 </ul>14 </div>15</details>See the top right corner of https://latest-with-plugins.datasette.io/ for a demo.
This displays an SVG icon which, when clicked, expands to show the menu. The SVG icon uses aria-labelledby="nav-menu-svg-title" role="img" and a <title id="nav-menu-svg-title"> element for accessibility.
I styled the menu using a variant of the following CSS:
1details.nav-menu > summary {2 list-style: none;3 display: inline;4 position: relative;5 cursor: pointer;6}7details.nav-menu > summary::-webkit-details-marker {8 display: none;9}10details .nav-menu-inner {11 position: absolute;12 top: 2rem;13 left: 10px;14 width: 180px;15 z-index: 1000;16 border: 1px solid black;17}18.nav-menu-inner a {19 display: block;20}list-style: none; hides the default reveal arrow from most browsers. ::-webkit-details-marker { display:none } handles the rest.
The summary element uses position: relative; and the details .nav-menu-inner uses position: absolute - this positions the open dropdown menu in the right place.
Click outside the box to close the menu#
The above uses no JavaScript at all, but comes with one downside: it’s usual with menus to clear them if you click outside the menu, but here you need to click on the exact icon again to hide it.
I solved that with the following JavaScript, run at the bottom of the page:
1document.body.addEventListener('click', (ev) => {2 /* Close any open details elements that this click is outside of */3 var target = ev.target;4 var detailsClickedWithin = null;5 while (target && target.tagName != 'DETAILS') {6 target = target.parentNode;7 }8 if (target && target.tagName == 'DETAILS') {9 detailsClickedWithin = target;10 }11 Array.from(document.getElementsByTagName('details')).filter(12 (details) => details.open && details != detailsClickedWithin13 ).forEach(details => details.open = false);14});