必须用 <nav> 包裹导航区,因其是HTML5语义化标签,影响SEO、键盘导航与读屏器识别;错误混入搜索框或滥用多个<nav>会破坏可访问性;推荐Flexbox布局并配合ARIA属性实现健壮、无障碍的下拉菜单。

必须用 <nav> 包裹,不是可选项
浏览器、读屏软件、搜索引擎靠语义标签识别“这是导航区”,<nav> 是 HTML5 明确规定的功能标签,不是视觉装饰。写成 <div class="nav"> 看起来一样,但键盘 Tab 顺序可能错乱,SEO 权重下降,辅助技术直接忽略整个区域。
常见错误包括:
- 把搜索框、登录按钮塞进同一个
<nav>—— 它们属于<search>或<form>,语义混杂会导致读屏器播报混乱 - 一个页面堆 5 个
<nav>—— 建议控制在 2–3 个(主导航 + 页脚链接组 + 面包屑),再多就该用aria-label明确区分 - 面包屑没加
aria-label="当前位置"—— 即使用了<nav>,不标注意图,读屏器仍可能读成“导航”而非“您当前所在位置”
display: flex 是当前最稳的横向布局方案
老式 float: left 或 display: inline-block 在响应式场景下极易崩:换行空格产生间隙、垂直对齐失效、父容器高度塌陷、移动端折行后错位。Flexbox 几乎没有这些副作用,现代浏览器全覆盖(IE11 除外)。
关键实操点:
立即学习“前端免费学习笔记(深入)”;
- 用
gap控制项间距,比给每个<li>加margin-right干净得多,也避免了“最后一个要不要清 margin”的纠结 -
justify-content: space-around比space-between更适合文字长度不均的导航项,两端留白更均衡 - 别在
<nav>上设white-space: nowrap—— 小屏会强制横向滚动,破坏体验;改用flex-wrap: wrap+ 媒体查询自然折行 - 如需兼容 IE11,
gap不可用,得退回到margin-right并手动处理最后一个子项的样式
当前页高亮必须靠 JS 或服务端动态注入
HTML 本身无法感知“我现在在哪一页”,硬编码 class="active" 到所有页面里是典型维护噩梦。路径匹配稍有偏差(比如 /about vs /about/ 或哈希路由 #/projects)就会失效。
前端简单判断逻辑(建议放 </body> 前):
document.querySelectorAll('nav a').forEach(link => {
const currentPath = window.location.pathname || '/';
const linkPath = new URL(link.href).pathname;
if (linkPath === currentPath) {
link.classList.add('active');
}
});
注意:
- 本地文件协议(
file://)下window.location.origin为空,需额外判断link.href.startsWith(currentPath) - 如果用的是前端路由(如 Vue Router、React Router),应监听路由变化事件,而非只在页面加载时执行一次
- CSS 高亮只需写
nav a.active { color: #007bff; },无需 JS 操控样式
下拉菜单必须配 ARIA 属性,否则对键盘和读屏用户不可用
纯 CSS 实现 :hover 展开二级菜单没问题,但视障用户无法知道这个按钮能展开什么,也无法用键盘 Tab 进入或 Enter 触发。ARIA 不是锦上添花,是功能必需。
最小必要 ARIA 组合:
- 触发按钮(如
<button>或带role="button"的<a>)加aria-expanded="false"和aria-controls="submenu-id" - 下拉容器(
<ul id="submenu-id">)加role="menu",每项加role="menuitem" - JS 切换展开状态时,同步更新
aria-expanded值,并确保焦点管理(展开后focus()到第一个菜单项) - 别只依赖
display: none隐藏下拉内容 —— 应配合aria-hidden="true",否则读屏器可能仍尝试读取
复杂点往往被忽略:下拉菜单收起时,焦点应回到触发按钮;键盘方向键(↑↓→←)需支持菜单内导航;ESC 键必须能关闭菜单。这些不是“增强体验”,而是可访问性的底线。



















