最推荐用 click 事件配合 classList.toggle() 控制菜单显隐;需配合 aria-expanded、aria-hidden、hidden 属性及焦点管理,确保无障碍支持。

直接用 click 事件配合 classList.toggle() 控制菜单显隐,是最轻量、兼容性最好、也最不容易出错的方式。别一上来就搞 visibility 或 opacity 动画,那会干扰可访问性和点击穿透。
为什么不用 display: none 直接切?
因为 display: none 会让元素完全脱离文档流,屏幕阅读器跳过、focus() 失效、键盘 Tab 无法到达——对无障碍是硬伤。正确做法是用 aria-expanded + aria-hidden 配合 max-height 或 height 过渡(或 JS 控制 hidden 属性)。
-
display: none看似简单,但会破坏键盘导航和读屏支持 - 推荐用
hidden属性:语义清晰、原生支持、不影响布局且自动被读屏识别 - 若需动画,用
max-height替代height,避免height: 0 → auto无法过渡
button 必须加 aria-controls 和 aria-expanded
否则屏幕阅读器不知道这个按钮控制哪个菜单、当前状态是展开还是收起。这两个属性不是可选的,是 WCAG 2.1 的强制要求。
-
aria-controls值必须和菜单容器的id完全一致(比如aria-controls="nav-menu"对应<div id="nav-menu">) -
aria-expanded初始值要和菜单实际状态同步(默认收起就设false) - JS 中每次切换都要同步更新这两个属性,不能只改样式
点击区域太小?别只靠 padding 撑开
移动端手指点不准、桌面端鼠标悬停易误触,本质是点击目标尺寸不达标。WCAG 要求最小触控区域 ≥ 44×44px,光调 padding 不够稳。
立即学习“前端免费学习笔记(深入)”;
- 给
button设置min-width: 44px; min-height: 44px;,再用line-height或flex居中文本 - 避免用
span或div模拟按钮——没有原生焦点、键盘Enter/Space不触发 - 如果图标按钮没文字,必须加
aria-label,例如aria-label="打开主菜单"
最易被忽略的是:菜单展开后,焦点没自动移到第一个可交互项上。用户按 Tab 键会从按钮跳到页面底部,体验断裂。手动调用 menuElement.querySelector('a, button').focus() 才算闭环。



















