最稳的导航结构是,因其语义正确、可访问性好、seo友好且默认支持键盘导航;flex布局侧边栏需设column方向与明确高度,子项用padding而非height;子菜单推荐max-height过渡动画并同步更新aria-expanded状态。

直接用 <nav></nav> 包 <ul></ul> + <li> + <a></a> 是最稳的起点,别一上来就套框架或写 div 堆砌结构。
为什么必须用 <nav><ul><li><a></a></li></ul></nav> 而不是 <div> 套链接
<p>语义错误会导致可访问性崩溃,屏幕阅读器会跳过纯 <code><div> 导航;SEO 也会弱化导航权重。浏览器对 <code><ul><li></ul> 的默认列表行为(如 tab 键顺序、焦点管理)是开箱即用的,而 <div> 需手动补全 <code>role="navigation"、tabindex、aria-expanded 等一堆属性。
-
<nav></nav> 明确告诉浏览器“这是主导航区”,不是普通容器
-
<ul></ul> 表达“一组并列选项”,比用 <div class="menu-item"> 更符合数据逻辑
<li>每个 <code><a></a> 必须带 href(哪怕只是 href="#" 或 href="javascript:;"),否则键盘用户无法聚焦或回车触发
display: flex 布局左侧菜单时的三个硬约束
Flex 方案适合固定宽度侧边栏,但容易因忽略细节导致子项错位或高度塌陷。
- 父容器(如
.sidebar)必须设 flex-direction: column,且 height: 100vh 或明确高度,否则 flex 子项不生效
- 每个
<li> 不要设 height,改用 padding 控制点击区域——否则文字换行时会被截断
-
<ul></ul> 必须清除默认 margin 和 padding,否则顶部/底部多出空白:ul { margin: 0; padding: 0; }
带折叠子菜单时,display: none 不是唯一解
用 display: none 切换子菜单最简单,但会破坏动画、影响焦点流,且对屏幕阅读器不友好。
- 推荐用
max-height + overflow: hidden 实现渐变收展,配合 transition 更自然
- 子菜单
<ul></ul> 必须加 role="group",父 <li> 加 aria-haspopup="true" 和 aria-expanded="false"
- JavaScript 切换时,务必同步更新
aria-expanded 值,否则读屏软件无法感知状态变化
- 不要用内联
style="display: none;" 初始化隐藏,统一用 CSS 类(如 .submenu--closed),便于维护和测试
真正难的不是写出能展开的菜单,而是让折叠状态在刷新后保持、让键盘用户能用 ↓/↑ 在子项间移动、让焦点不意外跳到页面底部——这些细节没处理,再漂亮的动画也是半残废导航。
<nav></nav> 明确告诉浏览器“这是主导航区”,不是普通容器<ul></ul> 表达“一组并列选项”,比用 <div class="menu-item"> 更符合数据逻辑
<li>每个 <code><a></a> 必须带 href(哪怕只是 href="#" 或 href="javascript:;"),否则键盘用户无法聚焦或回车触发
display: flex 布局左侧菜单时的三个硬约束
Flex 方案适合固定宽度侧边栏,但容易因忽略细节导致子项错位或高度塌陷。
- 父容器(如
.sidebar)必须设flex-direction: column,且height: 100vh或明确高度,否则flex子项不生效 - 每个
<li>不要设height,改用padding控制点击区域——否则文字换行时会被截断 -
<ul></ul>必须清除默认margin和padding,否则顶部/底部多出空白:ul { margin: 0; padding: 0; }
带折叠子菜单时,display: none 不是唯一解
用 display: none 切换子菜单最简单,但会破坏动画、影响焦点流,且对屏幕阅读器不友好。
- 推荐用
max-height+overflow: hidden实现渐变收展,配合transition更自然 - 子菜单
<ul></ul>必须加role="group",父<li>加aria-haspopup="true"和aria-expanded="false" - JavaScript 切换时,务必同步更新
aria-expanded值,否则读屏软件无法感知状态变化 - 不要用内联
style="display: none;"初始化隐藏,统一用 CSS 类(如.submenu--closed),便于维护和测试
真正难的不是写出能展开的菜单,而是让折叠状态在刷新后保持、让键盘用户能用 ↓/↑ 在子项间移动、让焦点不意外跳到页面底部——这些细节没处理,再漂亮的动画也是半残废导航。











