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

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



















