所有主级导航必须用包裹,菜单项用结构且需含href;横向排列优先用flex而非float;下拉菜单须支持键盘焦点流并正确使用aria属性;移动端需设置touch-action: manipulation和最小44×44px点击区域。

用 <nav></nav> 包住菜单,别用 <div class="menu">
<p>语义化不是加分项,是浏览器和读屏工具识别导航的唯一依据。不用 <code><nav></nav>,键盘用户按 Tab 时可能跳过整个菜单,SEO 也收不到结构信号。
- 所有主级导航(顶部、侧边)必须套
<nav></nav>,一个页面可有多个 <nav></nav>,但别嵌套
- 菜单项统一用
<ul><li><a></a></li></ul> 结构,<a></a> 必须带 href(哪怕只是 #),否则无法键盘聚焦
- 避免用
<span></span> 或 <button></button> 冒充链接——它们默认不可被 Tab 访问,且无原生跳转行为
CSS 实现横向排列时,display: flex 比 float 稳定得多
<nav></nav>,一个页面可有多个 <nav></nav>,但别嵌套<ul><li><a></a></li></ul> 结构,<a></a> 必须带 href(哪怕只是 #),否则无法键盘聚焦<span></span> 或 <button></button> 冒充链接——它们默认不可被 Tab 访问,且无原生跳转行为display: flex 比 float 稳定得多float 在响应式场景下容易塌陷、换行错位,尤其配合 margin 或字体缩放时。Flex 是目前最可控的方案,兼容性也已覆盖 IE11+(需加 display: -ms-flexbox)。
- 给
<ul></ul>加display: flex,子项<li>自动横排;用gap控制间距,比margin更干净 - 别对
<li>设float: left—— 它会让父<ul></ul>高度塌为 0,还得额外清浮 - 移动端折叠菜单如果用 JavaScript 切换
display: none,确保它作用在<ul></ul>上,而不是<nav></nav>,否则语义结构会断掉
下拉菜单必须处理焦点流和键盘操作
鼠标悬停展开的下拉菜单,在键盘模式下几乎必然卡死:焦点进不去、Esc 关不掉、Tab 跳过子项——这不是“体验问题”,是 WCAG 2.1 的强制要求。
- 用
aria-haspopup="menu"和aria-expanded标记触发按钮,屏幕阅读器才能告知用户“这是可展开菜单” - 下拉容器(
<ul></ul>)初始设visibility: hidden+position: absolute,不要用display: none,否则键盘焦点无法进入 - 按
Enter或Space展开后,焦点应自动移到第一个子菜单项;按Esc必须收起并把焦点移回触发按钮
移动端点击区域太小,touch-action: manipulation 能减少 300ms 延迟
iOS Safari 和部分安卓浏览器对 <a></a> 外的点击区域(比如只包了文字的 <span></span>)会加 300ms 延迟来判断是否双击缩放。这个延迟肉眼可感,且无法靠 fastclick 完全解决。
立即学习“前端免费学习笔记(深入)”;
- 给所有菜单项的
<a></a>加style="touch-action: manipulation;",这是最轻量的解法 - 确保点击热区至少 44×44px(iOS 最小推荐值),可用
padding扩展,别只靠字体大小撑开 - 如果用了
pointer-events: none在某层遮罩上,记得给菜单本身设pointer-events: auto,否则真点不中
菜单看似简单,但每个交互节点都连着可访问性、触控响应、焦点管理三条线。漏掉任意一条,用户就可能卡在首页出不去。










