
响应式导航条代码示例:适配手机和桌面屏幕
导航条在桌面屏幕上通常可以横向展示多个链接;到了手机屏幕,如果仍然保持同样的布局,链接可能挤在一起、换行或超出视口。响应式导航条的目标不是单纯把链接藏进一个图标,而是根据可用空间调整布局,并让触屏、键盘和辅助技术用户都能理解和操作。
本文会从一个可以直接整合到普通网页中的示例开始,说明 HTML 结构、CSS 断点和 JavaScript 状态管理。示例不依赖框架或第三方库,适合企业网站、产品介绍页、文档站点等导航项数量有限的页面。它不是适用于所有场景的通用菜单组件;如果需要多级菜单、搜索、登录状态或复杂动画,通常应进一步设计交互和测试。
一、适用场景与设计边界
本文示例采用常见的“桌面横向导航、手机折叠导航”模式:桌面宽度下直接显示链接,窄屏下显示一个菜单按钮,点击后展开或收起链接。它适合一级导航比较简洁、页面空间有限的站点。
在开始写代码前,建议先确认几个问题:
- 导航项是否适合折叠?如果页面的主要任务要求用户随时看到所有入口,可以考虑让导航换行、改用滚动容器,或重新精简导航,而不是默认隐藏内容。
- 菜单是否只有一级?本文只实现展开和收起,不包含子菜单、悬停菜单、菜单外点击关闭、焦点陷阱或模态层行为。复杂交互不应只靠增加几个 CSS 类来拼凑。
- 断点应由内容决定。**以下示例以 48rem 作为桌面布局的起点。这只是演示值,不是设备型号标准。实际断点应依据导航链接的总宽度、字体、间距和站点容器宽度调整。
响应式断点描述的是布局空间,不是设备身份。手机、平板和桌面设备的视口尺寸会重叠,窗口也可能被缩窄;因此不要仅凭“设备是手机”决定显示哪种菜单。
二、可运行示例
下面是一份完整示例。将代码放进 HTML 文件即可在现代浏览器中查看。代码块中的 HTML 标签已经转义显示;复制到文件时,需把代码实体还原为对应字符(例如 < 还原为 < 字符)。CSS 和 JavaScript 不依赖外部资源。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>响应式导航示例</title>
<style>
:root {
font-family: system-ui, sans-serif;
color: #202124;
background: #fff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.site-header {
background: #172554;
color: #fff;
}
.navbar {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
min-height: 4rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.brand {
color: inherit;
font-weight: 700;
text-decoration: none;
white-space: nowrap;
}
.menu-toggle {
border: 1px solid #bfdbfe;
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
color: #fff;
background: transparent;
font: inherit;
cursor: pointer;
}
.menu-toggle:focus-visible,
.nav-list a:focus-visible,
.brand:focus-visible {
outline: 3px solid #fbbf24;
outline-offset: 3px;
}
.nav-list {
display: none;
list-style: none;
margin: 0;
padding: 0.25rem 0 1rem;
flex-direction: column;
gap: 0.25rem;
}
.nav-list.is-open {
display: flex;
}
.nav-list a {
display: block;
padding: 0.65rem 0.75rem;
border-radius: 0.25rem;
color: inherit;
text-decoration: none;
}
.nav-list a:hover {
background: #263b70;
}
.page-content {
width: min(100% - 2rem, 72rem);
margin: 2rem auto;
line-height: 1.6;
}
@media (min-width: 48rem) {
.navbar {
min-height: 4.5rem;
}
.menu-toggle {
display: none;
}
.nav-list,
.nav-list.is-open {
display: flex;
flex-direction: row;
align-items: center;
padding: 0;
gap: 0.25rem;
}
}
</style>
</head>
<body>
<header class="site-header">
<nav class="navbar" aria-label="主导航">
<a class="brand" href="/">示例站点</a>
<button
class="menu-toggle"
type="button"
aria-expanded="false"
aria-controls="primary-menu">
菜单
</button>
<ul class="nav-list" id="primary-menu">
<li><a href="/">首页</a></li>
<li><a href="/products/">产品</a></li>
<li><a href="/docs/">文档</a></li>
<li><a href="/about/">关于我们</a></li>
</ul>
</nav>
</header>
<main class="page-content">
<h1>页面标题</h1>
<p>缩窄浏览器窗口,或在手机上打开页面,测试导航条的布局与展开状态。</p>
</main>
<script>
const toggle = document.querySelector(".menu-toggle");
const menu = document.querySelector("#primary-menu");
const desktopQuery = window.matchMedia("(min-width: 48rem)");
function setMenuOpen(open) {
menu.classList.toggle("is-open", open);
toggle.setAttribute("aria-expanded", String(open));
}
toggle.addEventListener("click", () => {
const isOpen = toggle.getAttribute("aria-expanded") === "true";
setMenuOpen(!isOpen);
});
function syncMenuToViewport() {
if (desktopQuery.matches) {
setMenuOpen(false);
}
}
desktopQuery.addEventListener("change", syncMenuToViewport);
syncMenuToViewport();
</script>
</body>
</html>
示例中导航链接使用站点路径作为占位地址。接入项目时,应换成实际页面地址;如果项目部署在子目录下,根路径 /products/ 等可能不是正确地址,需按站点路由和部署配置修改。
三、结构与无障碍属性
导航区域使用 <nav> 表示,链接放在无序列表中。这些语义能帮助浏览器和辅助技术识别导航与链接关系。页面如果存在多个导航区域,例如页头主导航和页脚导航,应使用不同的 aria-label,以便区分。
展开控制使用真正的 <button>,而不是外观像按钮的普通 <div>。按钮原生支持键盘操作,并能表达可操作控件的语义。type="button" 可避免按钮位于表单中时意外提交表单。
aria-controls="primary-menu" 指明按钮所控制的菜单元素;aria-expanded 则应反映菜单当前是否展开。示例在每次切换时同时更新 CSS 类和 aria-expanded,避免视觉状态与辅助技术读取到的状态不一致。
折叠状态下,示例通过 display: none 隐藏菜单,因此菜单里的链接也不会参与正常的键盘焦点顺序。不要只把内容移出屏幕或设置透明度,却仍让不可见链接可以获得焦点;这会造成用户看不到当前焦点所在位置的问题。
示例在桌面宽度下隐藏菜单按钮并始终展示导航链接。若项目需要在桌面也提供折叠能力,应重新定义按钮的显示条件、键盘行为和状态同步,不能只删掉一条 display: none 后假定所有交互都自然成立。
四、CSS 布局与断点
导航容器使用 Flexbox 让品牌标识和菜单按钮分列两侧。窄屏时导航列表默认不显示,获得 is-open 类后改为纵向排列;进入媒体查询指定的宽度后,列表改为横向排列,按钮隐藏。
媒体查询里的 48rem 与 JavaScript 使用的媒体查询条件相同。两处条件必须保持一致,否则可能出现 CSS 已切换为桌面布局、JavaScript 却仍按手机状态处理的情况。调整断点时,应同时修改 CSS 与 JavaScript。
rem 单位会受根字号影响;在常见浏览器默认字号下,48rem 通常约为 768 CSS 像素,但不应把它当作固定设备宽度。用户的缩放设置和浏览器字号也会影响实际布局表现。测试时要覆盖断点两侧,而不只是用一台手机和一台桌面电脑。
:focus-visible 为键盘导航时的焦点提供清晰提示。不要为了视觉简洁而移除浏览器默认焦点样式,却不提供替代方案。颜色、文字大小和触控区域也应结合站点的无障碍要求检查;示例只是基础样式,并不自动保证符合所有无障碍标准。
五、JavaScript 状态同步
脚本只负责响应用户点击,以及在视口跨过断点时把菜单状态重置为关闭。点击时读取当前 aria-expanded,再通过 setMenuOpen 同步类名和 ARIA 状态。这样不会出现 CSS 显示菜单但属性仍报告收起的情况。
用户可能先在手机宽度展开菜单,再把浏览器拉宽到桌面。桌面布局会始终显示链接,因此脚本在媒体查询变化时清除展开类,并把按钮状态复位。按钮在桌面隐藏,重置状态也能避免之后缩回手机宽度时意外保留旧的展开状态。
本例使用 window.matchMedia() 监听断点变化。它是现代浏览器广泛支持的 Web API;如需兼容更旧的浏览器,应按项目支持范围检查其支持情况,并在需要时为媒体查询监听方式提供兼容处理。示例代码并未加入对所有历史浏览器的兼容层。
六、常见错误与处理方式
- 只写媒体查询,不实现点击行为。CSS 可以改变布局,但无法独自处理按钮点击后的状态。折叠菜单需要脚本或其他明确的交互实现。
- 用非按钮元素充当开关。给
div添加点击事件并不会自动获得按钮的键盘语义和操作方式。优先使用原生按钮,减少需要自行补齐的行为。 - ARIA 状态没有同步。如果只切换菜单样式而不更新
aria-expanded,辅助技术获取到的信息可能与屏幕显示不一致。反过来,只更新属性而不更新视觉状态也同样有问题。 - CSS 和 JavaScript 使用不同断点。例如 CSS 在 48rem 切换,脚本却在 768px 切换,可能因根字号或条件不一致而产生边界异常。应统一条件并在断点附近测试。
- 只按某个手机型号调布局。视口宽度会变化,用户也可能使用分屏或调整窗口大小。应根据内容宽度选择断点,并在多种尺寸下验证导航是否拥挤、遮挡或溢出。
- 依赖悬停打开菜单。触屏没有常规鼠标悬停操作,键盘用户也需要明确的打开方式。若交互依赖悬停,应提供等效的点击和键盘操作。
- 点击按钮后页面意外跳到顶部。这常见于用
<a href="#">冒充按钮。切换状态属于操作,应使用按钮;链接则用于导航到真实地址。 - 使用固定高度或绝对定位导致内容被遮挡。导航项增多、字体放大或页面本地化后,固定尺寸可能无法容纳内容。优先允许布局自然增长,并检查长文本和较大字号。
七、安全与参数说明
示例中的 48rem 是 CSS 布局断点,不是 JavaScript 参数校验,也不是安全边界。媒体查询只决定页面如何展示;它不能用于判断用户是否可信、限制访问权限或保护数据。权限控制必须由服务端等实际执行授权检查的系统负责。
导航链接的 href 是页面地址。静态示例中的固定地址本身不会执行任意脚本,但如果项目通过 JavaScript 把用户提供的数据拼进链接、HTML 或脚本中,就应根据数据来源和使用位置进行校验与安全处理。不要把不可信字符串直接写入 innerHTML,也不要把导航菜单的折叠状态误认为任何形式的安全控制。
本例没有接收 URL 参数,也没有使用第三方脚本、Cookie 或网络请求,因此不存在可供调节的菜单参数列表。断点、颜色、间距和导航地址都是示例样式或页面配置,应按实际站点修改;它们没有通用的数值限制。对断点的具体选择,需要通过内容布局和目标浏览器测试确定。
八、上线前检查清单
- 在断点以下、断点附近和断点以上分别检查布局,确认链接没有溢出或被遮挡。
- 用鼠标点击菜单按钮,并用键盘 Tab 定位、Enter 或空格操作,确认焦点清晰可见。
- 展开和收起菜单时检查
aria-expanded是否与视觉状态一致。 - 在菜单展开时调整窗口宽度,再缩回窄屏,确认状态不会异常残留。
- 检查真实导航地址、长链接、较大字号和页面语言变化,确认布局仍然可用。
- 使用实际目标浏览器和辅助技术进行验证;通过示例代码本身不能推断页面已经满足所有兼容性或无障碍要求。
一个可靠的响应式导航条依靠清晰的语义、与内容相符的断点和同步的交互状态,而不是依赖某个图标或固定屏幕尺寸。先明确导航的层级与使用方式,再用真实内容测试布局,通常比不断追加样式补丁更容易维护。



