Bootstrap 5 navbar contains dropdown menus.
Dropdown menus have underlined hotkeys like a
,n
,s
,e
:
<div class="btn-group">
<button type="button" class="btn btn-danger dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Action
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#"><u>A</u>ction</a></li>
<li><a class="dropdown-item" href="#">A<u>n</u>other action</a></li>
<li><a class="dropdown-item" href="#"><u>S</u>omething else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">S<u>e</u>parated link</a></li>
</ul>
</div>
How to activate menu item if key is pressed in keyboard? For example, if dropdowns menu is open, pressing A should act like Action menu item is clicked.
jsfiddle: https://jsfiddle.net/b6or2s5e/
Left and right arrows are already handled using
How to use left and right arrow keys to navigate in bootstrap navbar
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll('.dropdown').forEach(function (el) {
el.addEventListener('shown.bs.dropdown', function () {
document.addEventListener('keydown', function (e) {
const click = new MouseEvent("click", {
bubbles: true,
cancelable: true,
view: window,
})
if (e.key === "ArrowRight") {
const o = getNextSibling(e.target.closest('.dropdown'), '.dropdown')
if (!o) {
document.querySelector('.dropdown-toggle').dispatchEvent(click)
} else {
o.querySelector('.Xdropdown-toggle').dispatchEvent(click)
}
} else if (e.key === "ArrowLeft") {
const o = getPrevSibling(e.target.closest('.dropdown'), '.dropdown')
if (!o) {
const ar = document.querySelectorAll('.dropdown-toggle')
ar[ar.length - 1].dispatchEvent(click)
} else {
o.querySelector('.dropdown-toggle').dispatchEvent(click)
}
}
}, { once: true })
})
})
})
How to add hotkey handling also?
Here is an updated snippet with a dynamic approach by getting the underlined elements and using a forEach loop over the underlined elements nodelist to compare a keydown
event on the window with each underlined elements textContent converted to toLowerCase()
and then compared. If there is a match we run click()
on the matching element.
Can switch switch statement removed and findUnderlined called for any key if alt or ctrl is not pressed?
I believe you are asking if you can add control and/or alt keys, not 100% sure of your ask is here, but you can add any conditional in terms of what you want to control with keydown. Just add the logic to the conditional.
See further notes in snippet below...
document.addEventListener("DOMContentLoaded", function() {
// get drop-down menu
const dropdownMenu = document.querySelector('.dropdown-menu');
// get action button
const action = document.querySelector('#dropdown-btn');
// get the underlined elements as a nodelist
const sections = dropdownMenu.querySelectorAll('li u');
// helper function pass in underlined els and event key
const setClickForUnderlinedEls = (els, key) => {
// loop over underlined elements
els.forEach(sec => {
// conditional to compare the underlined
// elements text to lowercase
if (key === sec.textContent.toLowerCase()) {
// simulate a click on element
sec.click()
}
});
}
// event click on action button to open dropdown
action.addEventListener('click', (event) => {
// window event on keydown to check key
addEventListener('keydown', (e) => {
// make sure the dropdown is expanded
// using aria-expanded attribute
if (event.target.ariaExpanded !== 'false') {
// run helper function to compare the key and
// the underlined elements textContent
setClickForUnderlinedEls(sections, e.key);
}
});
})
});
document.addEventListener("DOMContentLoaded", function() {
const dropdownMenu = document.querySelector('.dropdown-menu');
const action = document.querySelector('#dropdown-btn');
const sections = dropdownMenu.querySelectorAll('li u');
const setClickForUnderlinedEls = (els, key) => {
els.forEach(sec => {
if (key === sec.textContent.toLowerCase()) {
sec.click()
}
});
}
action.addEventListener('click', (event) => {
addEventListener('keydown', (e) => {
if (event.target.ariaExpanded !== 'false') {
setClickForUnderlinedEls(sections, e.key);
}
});
})
});
#vertical-space,
#section1,
#section2,
#section3,
#section4,
#section5{
height: 100vh;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<!-- Example single danger button -->
<div class="btn-group" id="top">
<!--/ Added the id dropdown-btn for this example /-->
<button id="dropdown-btn" type="button" class="btn btn-danger dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Action
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#section1"><u>A</u>ction</a></li>
<li><a class="dropdown-item" href="#section2">A<u>n</u>other action</a></li>
<li><a class="dropdown-item" href="#section3"><u>W</u>e have a link</a></li>
<li><a class="dropdown-item" href="#section4"><u>S</u>omething else here</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="#section5">S<u>e</u>parated link</a></li>
</ul>
</div>
<!--/ added purely for example /-->
<div id="vertical-space">
</div>
<div id="section1">
<p>
You pressed the <strong>a</strong> button and instantiated a click on the <em>Action</em> button. <a href="#top">Back to top</a>
</p>
</div>
<div id="section2">
<p>
You pressed the <strong>n</strong> button and instantiated a click on the <em>Another action</em> button. <a href="#top">Back to top</a>
</p>
</div>
<div id="section3">
<p>
You pressed the <strong>w</strong> button and instantiated a click on the <em>We have a link</em> button. <a href="#top">Back to top</a>
</p>
</div>
<div id="section4">
<p>
You pressed the <strong>s</strong> button and instantiated a click on the <em>Something else here</em> button. <a href="#top">Back to top</a>
</p>
</div>
<div id="section5">
<p>
You pressed the <strong>e</strong> button and instantiated a click on the <em>Separate link</em> button. <a href="#top">Back to top</a>
</p>
</div>