导航栏下拉(子)菜单键盘可访问性实现及CSS切换箭头方案咨询
解决导航栏下拉菜单的键盘可访问性(无JS)+ CSS箭头切换
一、修复键盘可访问性问题(无需JavaScript)
咱们先修正你现有代码里的ARIA属性问题,再用CSS原生能力实现键盘可访问的下拉交互:
1. 清理冗余/错误的ARIA属性
- 你的
<nav>标签重复了role="navigation",删掉一个即可(其实<nav>本身就隐含导航语义,甚至可以完全去掉这个属性); - 顶级菜单容器
<ul>应该用role="menubar",子菜单的<ul>改用role="menu"(menubar是顶级导航容器,子菜单用menu更符合ARIA语义); - 给每个下拉触发链接保留
aria-haspopup="true",明确提示这是一个弹出菜单的触发器。
2. 用CSS :focus-within实现键盘触发的下拉显示
默认键盘用户用Tab键导航时,我们需要让下拉菜单在触发器获得焦点时自动展开。:focus-within伪类可以检测容器内任意元素获得焦点,正好用来控制子菜单的显示/隐藏:
/* 默认隐藏所有子菜单 */ .sf-menu .dropdown ul { display: none; position: absolute; top: 100%; left: 0; background: #fff; border: 1px solid #eee; min-width: 220px; padding: 8px 0; list-style: none; } /* 父容器有焦点/鼠标hover时,显示子菜单 */ .sf-menu .dropdown:focus-within ul, .sf-menu .dropdown:hover ul { display: block; } /* 多层子菜单的位置调整 */ .sf-menu .dropdown .dropdown ul { left: 100%; top: 0; }
3. 修复触发链接的默认跳转问题
你之前用href="#"作为触发器链接,按下Enter键会跳到页面顶部,影响体验。可以把触发链接改成href="javascript:void(0)"(无JS副作用),或者保留href="#"但用CSS配合:focus-within确保菜单展开逻辑不受影响。
二、CSS实现展开/折叠的箭头切换
我们用CSS伪元素添加箭头,再通过:focus-within和:hover状态切换箭头方向:
/* 给触发链接预留箭头空间 */ .sf-menu .dropdown > a { position: relative; padding-right: 22px; text-decoration: none; color: #333; display: block; padding: 10px 0; } /* 默认向下箭头 */ .sf-menu .dropdown > a::after { content: "▼"; position: absolute; right: 4px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; font-size: 12px; } /* 多层子菜单用向右箭头 */ .sf-menu .dropdown .dropdown > a::after { content: "▶"; } /* 菜单展开时旋转箭头 */ .sf-menu .dropdown:focus-within > a::after, .sf-menu .dropdown:hover > a::after { transform: translateY(-50%) rotate(180deg); } .sf-menu .dropdown .dropdown:focus-within > a::after, .sf-menu .dropdown .dropdown:hover > a::after { transform: translateY(-50%) rotate(90deg); }
三、修正后的完整代码示例
HTML(优化语义和ARIA属性)
<nav id="main-menu" aria-label="Main navigation" class="fixed"> <ul class="sf-menu fixed" id="menu" role="menubar"> <li class="dropdown" role="none"> <a href="javascript:void(0)" aria-haspopup="true" aria-expanded="false" role="menuitem">Υπουργείο</a> <ul role="menu" aria-label="Υπουργείο υπομενού"> <li role="none"><a href="#" role="menuitem">Όραμα και Αποστολή</a></li> <li role="none"><a href="#" role="menuitem">Υπουργός</a></li> <li role="none"><a href="#" role="menuitem">Γενικός Διευθυντής</a></li> <li role="none"><a href="#" role="menuitem">Προηγούμενοι Υπουργοί</a></li> <li role="none"><a href="#" role="menuitem">Οργανωτική Δομή</a></li> <li role="none"><a href="#" role="menuitem">Συμβούλια / Επιτροπές</a></li> </ul> </li> <li class="dropdown" role="none"> <a href="javascript:void(0)" role="menuitem">Επιδόματα και Παροχές</a> <ul role="menu" aria-label="Επιδόματα και Παροχές υπομενού"> <li role="none"><a href="#" role="menuitem">Ελάχιστο Εγγυημένο Εισόδημα και Δημόσιο Βοήθημα</a></li> <li class="dropdown" role="none"> <a href="javascript:void(0)" aria-haspopup="true" aria-expanded="false" role="menuitem">Οικογένειας και Παιδιού</a> <ul role="menu" aria-label="Οικογένειας και Παιδιού υπομενού"> <li role="none"><a href="#" role="menuitem">Ειδικό Βοήθημα Τοκετού σε Άγαμες Μητέρες</a></li> <li role="none"><a href="#" role="menuitem">Επίδομα Τέκνου, Επίδομα Μονογονεϊκής Οικογένειας</a></li> <li role="none"><a href="#" role="menuitem">Τιμητικό Επίδομα Πολύτεκνης Μάνας</a></li> <li role="none"><a href="#" role="menuitem">Επίδομα Πατρότητας</a></li> <li role="none"><a href="#" role="menuitem">Σχέδιο Παραχώρησης Ωφελημάτων σε Οικογένειες που Αποκτούν Τρίδυμα και Άνω</a></li> </ul> </li> <li role="none"><a href="#" role="menuitem">Εργασίας/Ανεργίας</a></li> </ul> </li> </ul> </nav>
CSS(完整样式)
/* 基础菜单布局 */ .sf-menu { list-style: none; padding: 0; margin: 0; display: flex; background: #fff; gap: 20px; padding: 0 16px; } .sf-menu .dropdown { position: relative; } /* 子菜单基础样式 */ .sf-menu .dropdown ul li { padding: 0 16px; } .sf-menu .dropdown ul li a { display: block; padding: 6px 0; text-decoration: none; color: #333; } /* 焦点样式(键盘用户必备) */ .sf-menu a:focus { outline: 2px solid #0066cc; outline-offset: 2px; }
关键注意事项
- 无JS方案依赖
:focus-within,现代浏览器(Chrome、Firefox、Safari 10.1+、Edge)都支持,兼容性没问题; - 这个方案只能支持Tab键导航(在所有链接间切换),没法实现WAI-ARIA菜单模式要求的箭头键遍历(上下左右切换菜单项),如果需要完整箭头键支持,还是得用少量JS监听键盘事件;
- 绝对不要去掉焦点样式(
outline),这是键盘用户判断当前焦点位置的核心标识。
内容的提问来源于stack exchange,提问作者fatsoua
相关产品推荐
相关产品推荐

