键盘聚焦a标签时如何为div.sub_menu添加display:block样式
键盘聚焦触发下拉子菜单显示实现方案
核心问题很明确:你已经获取到触发聚焦的<a>元素,对应的.sub_menu就是这个a标签所在li.dir的直接子元素,用原生DOM API可以直接精准定位,不需要复杂遍历。
实现思路
- 精准定位子菜单:从绑定事件的
<a>出发,先拿到父级li.dir节点,再查找这个li下的直接子级.sub_menu即可,不会误选其他嵌套层级的子菜单。 - 优化类名操作:不要直接全量赋值
className属性,用classListAPI做类名增删,避免覆盖元素上原有的类(比如示例里首页li自带的current类会被你原来的写法抹掉)。 - 补全交互闭环:除了focus事件,还要绑定blur失焦事件,焦点离开菜单项时自动关闭子菜单,同步更新aria无障碍属性,符合键盘操作的交互逻辑。
- 样式控制优先用类:比直接写行内style更易维护,如果你必须要加行内
display:block样式,也可以在拿到sub_menu元素后直接修改style属性。
参考代码
首先加基础默认样式,保证子菜单初始是隐藏的:
/* 初始隐藏子菜单 */ .sub_menu { display: none; } /* 激活状态显示子菜单 */ li.dir.menu_hover .sub_menu { display: block; }
修正后的JS逻辑:
const menuItems = document.querySelectorAll('li.dir'); menuItems.forEach(function(menuItem) { const trigger = menuItem.querySelector('a[aria-haspopup="true"]'); // 定位当前li下的直接子级sub_menu,兼容后续DOM结构调整 const subMenu = menuItem.querySelector(':scope > .sub_menu'); // 聚焦显示逻辑 trigger.addEventListener('focus', function(e) { menuItem.classList.add('menu_hover'); trigger.setAttribute('aria-expanded', 'true'); // 如果需要直接加行内样式,放开下面这行注释即可 // subMenu.style.display = 'block'; e.preventDefault(); }); // 失焦关闭逻辑 trigger.addEventListener('blur', function(e) { menuItem.classList.remove('menu_hover'); trigger.setAttribute('aria-expanded', 'false'); // 对应行内样式写法放开下面注释 // subMenu.style.display = 'none'; }); });
关键注意点
用
:scope > .sub_menu选择器的好处是,只要.sub_menu是li的直接子元素,不管你后续在a标签和sub_menu之间加什么其他元素(比如下拉箭头、装饰标签),选择逻辑都不会失效,比取nextElementSibling的容错率更高。
- 如果你需要兼容非常老的不支持
:scope的浏览器,直接把定位sub_menu的代码换成const subMenu = trigger.nextElementSibling;即可,对应你现在的DOM结构完全可以正常运行。 - 原有逻辑里判断父节点className的分支完全没必要,因为你一开始选择的就是所有
li.dir节点,父节点必然是dir类,不需要额外判断。 - 绑定事件的时候不需要用
Array.prototype.forEach.call转类数组,现代浏览器直接调用NodeList的forEach方法就可以跑。
内容的提问来源于stack exchange,提问作者qwerty
相关产品推荐
相关产品推荐

