使用 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的主要内容,如果未能解决你的问题,请参考以下文章

超赞Win10日历悬停效果,爱了爱了(使用HTMLCSS和vanilla JS)

使用键数组遍历嵌套的 Ruby 哈希

Vanilla JS 从单元格中获取最低值并更改背景颜色

Vue.js - 如何显示所有属性键和嵌套属性值?

Python ❀ 字典

Python ❀ 字典