一段可直接落地的导航栏

导航栏是几乎所有网站的标配,也是响应式改造里最容易返工的部分:桌面端好好的菜单,到手机上要么挤成一团,要么只能靠浏览器自带的"放大"勉强操作。下面这个组件把常见需求一次打包——粘性置顶、移动端汉堡菜单、下拉子菜单、当前页面高亮,外加 CTA 按钮和无障碍标签,复制即可用。

组件不含任何框架依赖,纯 HTML/CSS/JavaScript 实现,方便接入任意项目。它适合三类典型站点:内容型博客(菜单简单、突出阅读)、企业展示站(用"服务"下拉做分类)、带转化的营销页(用 CTA 按钮引导咨询)。不同站点只需调整菜单结构与高亮逻辑,布局骨架可以复用。整体验收时,可以对照 响应式布局验收清单 逐项检查。

HTML

<nav class="navbar">
  <div class="nav-container">
    <a href="/" class="nav-logo">
      <img src="logo.svg" alt="Logo" height="32">
    </a>

    <button class="nav-toggle" aria-label="切换菜单">
      <span></span>
      <span></span>
      <span></span>
    </button>

    <ul class="nav-menu">
      <li><a href="/" class="active">首页</a></li>
      <li class="dropdown">
        <a href="/services">服务 ▾</a>
        <ul class="dropdown-menu">
          <li><a href="/web-design">网页设计</a></li>
          <li><a href="/hosting">主机托管</a></li>
          <li><a href="/seo">SEO 优化</a></li>
        </ul>
      </li>
      <li><a href="/about">关于</a></li>
      <li><a href="/blog">博客</a></li>
      <li><a href="/contact" class="nav-cta">联系我们</a></li>
    </ul>
  </div>
</nav>

CSS

/* 基础样式 */
.navbar {
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,.1);
  position: sticky;
  top: 0;
  z-index: 1000;
}

.nav-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}

.nav-menu {
  display: flex;
  list-style: none;
  gap: 8px;
  margin: 0;
  padding: 0;
}

.nav-menu a {
  text-decoration: none;
  color: #333;
  padding: 8px 16px;
  border-radius: 6px;
  transition: background .2s;
}

.nav-menu a:hover,
.nav-menu a.active {
  background: #f0f0f0;
  color: #4F46E5;
}

.nav-cta {
  background: #4F46E5;
  color: #fff !important;
}

.nav-cta:hover {
  background: #4338CA !important;
}

/* 下拉菜单 */
.dropdown { position: relative; }

.dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: #fff;
  box-shadow: 0 4px 12px rgba(0,0,0,.1);
  border-radius: 8px;
  min-width: 180px;
  list-style: none;
  padding: 8px;
}

.dropdown:hover .dropdown-menu {
  display: block;
}

/* 汉堡按钮 */
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}

.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: #333;
  transition: transform .3s;
}

/* 移动端 */
@media (max-width: 768px) {
  .nav-toggle { display: flex; }

  .nav-menu {
    display: none;
    position: absolute;
    top: 64px;
    left: 0;
    right: 0;
    background: #fff;
    flex-direction: column;
    padding: 16px;
    box-shadow: 0 4px 12px rgba(0,0,0,.1);
  }

  .nav-menu.active { display: flex; }

  .dropdown-menu {
    position: static;
    box-shadow: none;
    padding-left: 16px;
  }
}

JavaScript

document.addEventListener('DOMContentLoaded', () => {
  const toggle = document.querySelector('.nav-toggle');
  const menu = document.querySelector('.nav-menu');

  toggle.addEventListener('click', () => {
    menu.classList.toggle('active');
    toggle.setAttribute('aria-expanded',
      toggle.getAttribute('aria-expanded') === 'false' ? 'true' : 'false'
    );
  });

  // 点击菜单外关闭
  document.addEventListener('click', (e) => {
    if (!e.target.closest('.navbar')) {
      menu.classList.remove('active');
    }
  });
});

功能特性

  • ✅ 粘性导航(Sticky Header)
  • ✅ 移动端汉堡菜单
  • ✅ 下拉菜单(子导航)
  • ✅ 当前页面高亮(.active)
  • ✅ 点击外部关闭菜单
  • ✅ ARIA 无障碍标签
  • ✅ CTA 按钮样式

接入要点

拿到模板后接入时注意三个细节。第一,logo 路径与菜单链接要替换成你的真实地址;第二,把 .active 类加到当前页面对应的菜单项上,高亮由纯 CSS 完成,不需要额外 JS;第三,如果项目已经在用 Bootstrap、Tailwind 之类的框架,可以只保留交互逻辑,把样式换成框架类名。

如果菜单需要服务端渲染,比如配合 Next.js 或 Vue 3,建议直接把当前路由渲染成 .active,避免刷新后高亮丢失。

扩展与定制

基础的骨架有了,剩下按站点需要做三件常见的扩展:

多级下拉。 服务分类超过一层时,在 .dropdown-menu 里再加一组 <ul> 并让二级菜单横向浮出。注意移动端断点内要把多级下拉也改回纵向堆叠,否则触屏上很难操作。

搜索框。 把搜索表单放进 .nav-container,在移动端默认隐藏,点击搜索图标再展开。搜索框宽度用 min(320px, 100%) 保证小屏不溢出。

暗色模式。 用 CSS 变量接管背景和文字颜色,例如:

.navbar { background: var(--nav-bg, #fff); }
@media (prefers-color-scheme: dark) {
  :root { --nav-bg: #1f2937; --nav-text: #f9fafb; }
}

滚动反馈。 向下滚动时让导航收起、向上滚动时重新露出,既能提升阅读空间,也能避免 position: sticky 在长页面占位。这类交互要配合 requestAnimationFrame 做节流,避免频繁读写布局。

字体、间距、图标这类视觉细节,可以交给设计规范统一处理,参考 设计系统组件 和 现代 CSS 布局。

无障碍细节

  • aria-expanded 会随菜单开关同步更新,读屏软件能感知菜单状态;
  • 汉堡按钮的 aria-label="切换菜单" 明确了按钮用途;
  • 下拉目前依赖 hover,建议补充 :focus-within,让键盘用户也能展开子菜单:
.dropdown:focus-within .dropdown-menu { display: block; }
  • 移动端菜单打开后,可以把焦点移到第一个菜单项,避免键盘用户"迷失"在页面里。完整的无障碍规范可参考 网站无障碍指南。

性能与兼容提示

  • 全站菜单建议在关键页面内联首屏 CSS,避免导航在样式加载前闪动(FOUC);
  • 图标用内联 SVG 或 iconfont,比一张整图少一次请求;
  • 菜单项不超过 8 个时,可以不引入 JS 框架,原生代码已足够;
  • 老版本浏览器(如 IE)需要 @supports 或降级样式兜底,目前主流浏览器均已支持 flex。

常见问题

为什么用 position: sticky 而不是 fixed 导航在页面顶部时两者表现一致,但 sticky 会保留自身占位、不脱离文档流;当导航不是页面第一个元素时,fixed 会把内容顶到导航下方造成遮挡,sticky 不会有这个问题。

下拉菜单在触屏上点不开? 默认实现依赖 hover,触屏的第一次点击会先触发 hover 而不是跳转。建议在移动端断点内把下拉改成点击切换,或直接展示静态子菜单。

一级菜单项多了怎么办? 超过 6-7 项时横向空间吃紧。常见做法是把次要入口收进"更多"下拉,或用图标加文字的组合压缩宽度。

页面还有别的吸顶元素(如公告栏)怎么办?.navbar 设置 top: 40px(公告栏高度)即可让导航吸附在公告栏下方;多个吸顶元素并存时,按层级用 z-index 排序即可。

点击汉堡按钮后菜单没有展开? 多半是 JS 没加载成功,或 .nav-toggle.nav-menu 的类名被框架覆盖。先打开控制台确认没有报错,再检查 menu.classList.toggle('active') 对应的选择器是否与实际 DOM 一致。

参考:MDN <nav> 元素 — https://developer.mozilla.org/docs/Web/HTML/Element/nav ;WAI-ARIA Menu Pattern — https://www.w3.org/WAI/ARIA/apg/patterns/menu/