使用 vanilla js 使用箭头键遍历嵌套 uls 时跳过 ul 的第一个 li
Posted
技术标签:
【中文标题】使用 vanilla js 使用箭头键遍历嵌套 uls 时跳过 ul 的第一个 li【英文标题】:first li of ul skipped when traversing nested uls with arrow keys using vanilla js 【发布时间】:2019-05-17 02:11:37 【问题描述】:我有一个多级下拉列表,我想使用 vanilla js 使用箭头键进行导航。当我键入列表时,第一个下拉列表的第一个链接被跳过。因为它的父 li 没有兄弟姐妹,所以遍历到这里就停止了。一定有什么逻辑错误,但过了很长时间,我看不到它。
我已尝试以多种方式引用该链接后应获得焦点的按钮,但没有成功
我希望遍历从子菜单 3 到子菜单 2 到子菜单 1 到菜单 2 旁边的按钮。相反,它从子菜单 2 到按钮,跳过子菜单 1
<style>
ul.nav display: flex; list-style: none;
body:not(.js) ul.sub
position: absolute;
left: -9999em;
body:not(.js) li.has-sub:hover > ul.sub,
body:not(.js) li.has-sub:focus-within > ul.sub,
body:not(.js) li.has-sub ul:hover
position: relative;
left: 0;
body.js .hidden
position: absolute;
left: -9999em;
.hidden
position: absolute;
left: -9999em;
body.js .visible
position: relative;
left: 0;
.visible
position: relative;
left: 0;
</style>
</head>
<body>
<ul class="nav" id="main_nav">
<li>
<a href="#">Menu 1</a>
</li>
<li class="has-sub">
<a href="#">Menu 2</a>
<!-- <button class="trigger">More</button> -->
<ul class="sub">
<li><a href="#">Sub-Menu 1</a></li>
<li class="has-sub">
<a href="#">Sub-Menu 2</a>
<!-- <button class="trigger">More</button> -->
<ul class="sub">
<li>
<a href="#">Nested-Menu 1</a>
</li>
<li>
<a href="#">Nested-Menu 2</a>
</li>
<li>
<a href="#">Nested-Menu 3</a>
</li>
</ul>
</li>
<li>
<a href="#">Sub-Menu 3</a>
</li>
</ul>
</li>
<li><a href="#">Menu 3</a></li>
</ul>
<script type="text/javascript">
document.querySelector('body').classList.add('js');
//ADD BUTTONS
let triggers = document.querySelectorAll('li.has-sub > a');
for(var i=0; i < triggers.length; i++)
let toggler = document.createElement('button');
toggler.setAttribute("class", "trigger");
triggers[i].parentNode.insertBefore(toggler, triggers[i].parentNode.querySelector('ul.sub'));
//HIDE SUBS BY DEFAULT
let subs = document.querySelectorAll('ul.sub');
for(let a = 0; a < subs.length; a++)
subs[a].classList.add('hidden');
//ADD TABINDEX -1
var dropdownLinks = document.querySelectorAll('ul.sub a');
dropdownLinks.forEach(dropdownLink => dropdownLink.setAttribute("tabindex", "-1"));
var dropdownButtons = document.querySelectorAll('ul.sub button');
dropdownButtons.forEach(dropdownButton => dropdownButton.setAttribute("tabindex", "-1"));
//SHOW SUBS ON CLICK - DEFINE TOGGLE AND APPLY
var toggle = function(elem)
// elem.classList.toggle('visible');
if(elem.classList.contains('hidden'))
elem.classList.remove('hidden');
elem.classList.add('visible');
else if(elem.classList.contains('visible'))
elem.classList.remove('visible');
elem.classList.add('hidden');
;
var hide = function(elem)
if(elem.classList.contains('visible'))
elem.classList.remove('visible');
if(!elem.classList.contains('hidden'))
elem.classList.add('hidden');
var buttonTogglers = document.querySelector('.nav').querySelectorAll('button.trigger');
for(var f = 0; f < buttonTogglers.length; f++)
buttonTogglers[f].addEventListener("click", function()
toggle(this.nextElementSibling);
);
buttonTogglers[f].addEventListener("focusout", function()
//hides parent too which we don't want.
);
// var = document.querySelector("ul.nav");
// nav.addEventListener("keydown", function(e)
var subMamas = document.querySelectorAll('li.has-sub');
for(var g = 0; g < subMamas.length; g++)
subMamas[g].addEventListener("keydown", function(e)
var active = document.activeElement;
active = '';
if(e.keyCode === 40)
console.log('down');
if(document.activeElement.nextElementSibling)
if(document.activeElement.nextElementSibling.classList.contains('visible'))
document.activeElement.nextElementSibling.firstElementChild.firstElementChild.focus();
else
document.activeElement.parentElement.nextElementSibling.firstElementChild.focus();
else if (document.activeElement.parentElement.nextElementSibling)
document.activeElement.parentElement.nextElementSibling.firstElementChild.focus();
else
document.activeElement.parentElement.parentElement.firstElementChild.focus();
//UP
if(e.keyCode === 38)
console.log('up');
var active;
console.log('start' + document.activeElement.textContent );
if(document.activeElement.parentElement.previousElementSibling)
// console.log(active.parentElement.previousElementSibling);
active = document.activeElement.parentElement.previousElementSibling.firstElementChild;
active.focus();
console.log('finish' + active.textContent);
active = '';
else if(!document.activeElement.parentElement.previousElementSibling)
console.log('no sib');
active = document.activeElement.closest('.sub').previousElementSibling;
active.focus();
active = '';
//RIGHT
if(e.keyCode === 39)
console.log('right');
//LEFT
if(e.keyCode === 37)
console.log('left');
);
</script>
【问题讨论】:
您必须在您的问题中发布您的代码,而不是第三方网站:minimal reproducible example 感谢 Rob 的提醒;我已经编辑了我的问题以直接包含代码 是否可以删除反对票? 【参考方案1】:在这种情况下,将 eventListener 添加到导航而不是每个 li.has-sub 解决了问题。即删除:
var subMamas = document.querySelectorAll('li.has-sub');
for(var g = 0; g < subMamas.length; g++)
subMamas[g].addEventListener("keydown", function(e)
改为添加:
var = document.querySelector("ul.nav");
nav.addEventListener("keydown", function(e)
【讨论】:
以上是关于使用 vanilla js 使用箭头键遍历嵌套 uls 时跳过 ul 的第一个 li的主要内容,如果未能解决你的问题,请参考以下文章