我正在尝试为投资组合式网站创建一个可访问的导航菜单。当屏幕小于一定宽度(本演示为 768px)时,水平菜单导航消失并被“汉堡”取代。汉堡的父 div 具有 JavaScript onclick 和 onkeydown 函数,以便当单击或点击汉堡时,或者键盘用户将注意力集中在汉堡上并使用按空格键或 Enter 键时,会从包含垂直导航菜单的一侧打开一个 div“sidenav” .
但是,如果键盘用户继续按 Tab 键,焦点会继续向下移动到导航后面的页面,就好像“sidenav”不存在一样,当 Tab 键最终到达 sidenav 的顶部(它遇到的第一个元素)时是关闭按钮,按 Tab 键会关闭菜单,因此无法访问菜单项。
所以: 问题1:当按下汉堡键时,如何触发焦点“跳转”到新打开的导航?
问题 2:如何使“关闭”按钮忽略 Tab 键并仅使用空格键或 Enter 键?
这是我的菜单的简化版本(您可能需要全屏才能看到水平菜单)。
/* detect keyboard users */
function handleFirstTab(e) {
if (e.keyCode === 9) { // the "I am a keyboard user" key
document.body.classList.add('user-is-tabbing');
window.removeEventListener('keydown', handleFirstTab);
}
}
window.addEventListener('keydown', handleFirstTab);
/* Open Sidenav
-------------------*/
function openNav() {
let element = document.querySelector('ul.menucontent');
if (element.classList.contains('menucontent')) {
element.classList.remove('menu-a');
element.classList.add('menu-b');
};
let element3 = document.querySelector('div.sidenav');
let element4 = document.querySelector('.closebtn');
if (element3.classList.contains('sidenav')) {
element3.style.width = "350px";
element4.style.visibility = "visible";
};
document.getElementById('vmenu').focus();
}
function closeNav() {
let element = document.querySelector('ul.menucontent');
if (element.classList.contains('menucontent')) {
element.classList.remove('menu-b');
element.classList.add('menu-a');
};
let element3 = document.querySelector('div.sidenav');
let element4 = document.querySelector('.closebtn');
if (element3.classList.contains('sidenav')) {
element3.style.width = "0";
element4.style.visibility = "hidden";
};
}
// Toggle content
for (const selector of [".toggle-btn",]) {
const toggleButtons = [...document.querySelectorAll(selector)];
for (const toggleButton of toggleButtons) {
toggleButton.addEventListener('click', () => {
toggleButtons.filter(b => b !== toggleButton).forEach(b => {
b.nextElementSibling.classList.remove('reveal-content');
});
toggleButton.nextElementSibling.classList.toggle('reveal-content');
});
};
}
for (const selectorTwo of [".close-btn",]) {
const closeButtons = [...document.querySelectorAll(selectorTwo)];
for (const closeButton of closeButtons) {
closeButton.addEventListener('click', () => {
closeButton.parentElement.classList.toggle('reveal-content');
});
};
}
body.user-is-tabbing button > a:focus {
border: none;
}
body:not(.user-is-tabbing) a:focus,
body:not(.user-is-tabbing) button:focus,
body:not(.user-is-tabbing) input:focus,
body:not(.user-is-tabbing) select:focus,
body:not(.user-is-tabbing) textarea:focus {
outline: none;
}
.container-fluid,
.container {
margin-right: auto;
margin-left: auto;
width: 100%;
}
.d-block
.d-none {
display: none;
}
.sidenav {
height: 100%;
width: 0;
position: fixed;
z-index: 996;
top: 0;
left: 0;
background-color: #fff;
overflow-x: hidden;
transition: 0.5s;
padding: 1rem 0 0;
box-shadow: 0 2px 5px #acaaaa;
}
.trigram {
position: relative;
top: 0;
left: 0;
margin-bottom: 1rem;
padding: 0;
background: transparent;
z-index: 995;
width: 2rem;
}
.burger {
position: relative;
border-top: 0.15rem solid green;
border-bottom: 0.15rem solid green;
background: transparent;
height: 1.5rem;
width: 2rem;
}
.burger::after {
position: absolute;
content: "";
border-top: 0.15rem solid green;
top: 40%;
left: 0;
width: 2rem;
}
.sidemenu {
position: relative;
top: 5rem;
}
.mm ul {
list-style: none;
}
.mm li {
margin: 1rem 0;
padding: 0 0 0 1rem;
}
.mm .menucontent.menu-a {
display: none;
}
.mm .menucontent.menu-b {
display: flex;
display: -webkit-flex;
flex-direction: column;
justify-content: normal;
margin: 0 0 1rem;
padding: 0;
position: relative;
top: 0;
z-index: 997;
overflow-y: auto;
}
.closebtn {
border-bottom: none;
font-size: 2.25rem;
margin: 0;
position: absolute;
top: 2rem;
right: 2rem;
z-index: 998;
}
@media only screen and (min-width: 768px){
.d-none {
display: none;
}
.d-md-block {
display: block;
}
.trigram {
display: none;
}
.mm .menucontent.menu-a {
position: relative;
padding: 0;
margin: 0 auto;
white-space: nowrap;
display: flex;
}
.mm .menucontent.menu-a,
.mm .menucontent.menu-b {
flex-direction: row;
justify-content: center;
}
.mm li {
padding: 0 0.5rem;
}
.main-menu-container {
border-top: 2px solid green;
border-bottom: 2px solid green;
padding: 0.5rem 0;
margin: 1rem 0;
}
}
<div id="sidenav" class="sidenav">
<div id="closebtn" class="closebtn">
<a href="javascript:void(0)" onclick="closeNav()" onkeydown="closeNav()" role="button" tabindex="0" aria-label="close navigation">×</a>
</div>
<div id="vmenu" class="sidemenu d-md-none mm">
<nav aria-label="Main Navigation" class="menuouter ">
<ul class="menucontent menu-a" role="menubar">
<li class="item-101 default current active single top-level" role="none" tabindex="-1">
<a href="#" title="Side menu Home" class="icon-home">Side menu Home</a>
</li>
<li class="item-128 single" role="none" tabindex="-1">
<a href="#" title="Side menu page 2">Side menu page 2</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="container-fluid menu-outer d-block d-md-none">
<div id="trigram" class="trigram" role="button" tabindex="0" aria-label="open navigation" aria-controls="sidenav" aria-haspopup="true" onclick="openNav()" onkeydown="openNav()">
<div class="burger" style="cursor:pointer" > </div>
</div>
</div>
<div class="container-fluid d-none d-md-block">
<div class="main-menu-container">
<div id="hmenu" class="row d-none d-md-block main-menu mm">
<nav aria-label="Main Navigation" class="menuouter ">
<ul class="menucontent menu-a" role="menubar">
<li class="item-101 default current active single top-level" role="none" tabindex="-1">
<a href="#" title="Horizontal menu Home" class="icon-home">Horizontal menu Home</a>
</li>
<li class="item-128 single" role="none" tabindex="-1">
<a href="#" title="Horizontal menu page 2">Horizontal menu page 2</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div>This is some text. It has a link in it: <a href="#">This is the first link</a></div>
<div>Here is some more text with more links. It has a link in it: <a href="#">This is the second link</a>. Integer mauris sem, convallis ut, consequat in, sollicitudin sed, leo.</div>
<div>Sed lacus velit, consequat in, ultricies sit amet, malesuada et, diam. Integer mauris sem, convallis ut, consequat in, sollicitudin sed, leo. <a href="#">This is the third link </a>Cras purus elit, hendrerit ut, egestas eget, sagittis at, nulla. Integer justo dui, faucibus dictum, convallis sodales, accumsan id, risus. Aenean risus. Vestibulum scelerisque placerat sem.</div>
最佳答案
关于“焦点捕获”的好文章:https://medium.com/@im_rahul/focus-trapping-looping-b3ee658e5177
这是一个可能有帮助的 stackoverflow 答案:Vanilla javascript Trap Focus in modal (accessibility tabbing )
这就是我对模态框的做法:
const btnOpenEmailSignup = document.getElementById('env'); //This is the button that opens the modal
const modalOverlay = document.getElementById('modalOpenEmailSignup'); //Modal specific--won't need!
const btnClose = document.getElementById('close'); //Modal specific--won't need!
let focusedElementBeforeModal;
const toggleModal = function modalToggel() {
modalOverlay.classList.toggle('show-modal'); //Modal specific--won't need!
// ***** Trap focus ***** //
// Save current focus--*You might not need this*--
focusedElementBeforeModal = document.activeElement;
// Listen and trap the keyboard
modal.addEventListener('keydown', trapTabKey);
// Find all focusable children (not all of these will be needed, but keeping them in shouldn't hurt)
const focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]';
let focusableElements = modal.querySelectorAll(focusableElementsString);
// convert NodeList to Array
focusableElements = Array.prototype.slice.call(focusableElements);
const emailField = document.getElementById('email'); //This const is the one that will get focus on opening the modal.
const firstTabStop = focusableElements[0];
var lastTabStop = focusableElements[focusableElements.length - 1];
// Focus to email field
emailField.focus();
function trapTabKey(e) {
// Check for tab key press
if (e.keyCode === 9) {
// SHIFT + TAB
if (e.keyShift) {
if (document.activeElement === firstTabStop) {
e.preventDefault();
lastTabStop.focus();
}
// TAB
} else {
if (document.activeElement === lastTabStop) {
e.preventDefault();
firstTabStop.focus();
}
}
}
}
// ***** Trap focus end ***** //
一个注意事项:这似乎只在一个方向上起作用(“向下”,按 Tab 键,而不是“向上”,按 Shift+Tab)。目前还无法使其以其他方式工作。
关于javascript - 标签可访问的弹出导航的两个问题我使用了一些建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66080384/