简介:侧边导航栏是Bootstrap3中常用且灵活的组件,适用于内容层级丰富的后台界面与长文档站点。围绕这一组件,压缩包提供了一套可直接运行的示例,面向具备HTML/CSS基础、希望快速搭建响应式侧边栏的前端学习者或开发者。包内共9个文件、约170KB,包含1个HTML示例页、Bootstrap核心CSS与JS、jQuery库,以及glyphicons字体文件(woff/eot/ttf/svg等),覆盖页面结构、样式定义、交互脚本与图标呈现所需素材。压缩包以sidebar为主目录,结构简洁,便于对照理解导航栏的标签结构、折叠按钮与响应式行为。目前已有5179人学习下载,适合用来掌握Bootstrap3导航组件的工作方式,并在此示例上扩展自定义样式、子菜单或滚动监听等进阶功能。
1. 侧边导航栏:老后台项目最省事的布局方案
接手一个还停在 Bootstrap 3 的老后台项目,客户点名要加侧边导航栏。打开代码一看,页面全是静态 HTML + jQuery,没有前端工程化,也没有 Vue 或 React。这时候最省事的方案不是引入一套新框架,而是在现有结构上做一个「bootstrap3 侧边导航栏」组件:左侧固定菜单、右侧内容区、多级展开收起,再带上滚动高亮当前章节。这份资源就是一套开箱即用的实现,HTML、CSS、JS 全部独立,直接拷到任意 Bootstrap 3 页面里就能跑。适合三类人:老项目维护者、做后台管理模板的开发者,以及想学 Bootstrap 3 组件写法的新手。下面照着一线拆项目的习惯,把这个侧边栏从结构到改造完整讲一遍。
2. 项目结构与技术选型:Bootstrap 3 + jQuery 的轻量组合
2.1 资源包里的文件构成与职责划分
这套侧边导航栏不是一个 npm 包,也不是构建产物,而是一个可以直接扔进静态页面用的完整组件。解压后核心文件一共四个,加上一个可选的示例页,各自职责很清晰:
| 文件 | 职责 |
|---|---|
| index.html | 示例页面,包含侧边栏结构、内容区占位文字和依赖引入 |
| css/sidebar.css | 侧边栏专属样式,含固定定位、菜单缩进、激活态高亮、小屏折叠 |
| js/sidebar.js | 菜单展开收起、滚动监听高亮、移动端抽屉切换的逻辑 |
| css/bootstrap.min.css | Bootstrap 3 基础样式(资源包自带,版本 3.4.1) |
| js/jquery.min.js | jQuery 依赖(Bootstrap 3 的 JS 组件必需) |
我通常建议把 sidebar.css 和 sidebar.js 单独拎出来,而不是塞进全局样式里。原因很简单:侧边栏的样式规则很多带有 scoping 前缀(比如 .sidebar-nav),如果和全局样式混在一起,后期调整布局时很容易互相覆盖。单独文件也方便在其他项目里直接复用,复制两个文件加一段引入就行。
index.html 里的依赖引入顺序需要注意,Bootstrap 3 的 JS 组件强依赖 jQuery,所以顺序必须是「jQuery → Bootstrap JS → sidebar.js」。sidebar.js 不依赖 Bootstrap 的 JS 文件,但它要操作 DOM,所以放在 jQuery 之后即可。资源包里的示例页已经写好了正确顺序,照着复制就行。
2.2 为什么还选 Bootstrap 3:老项目集成的兼容性考量
可能有人会问,都什么年代了还在用 Bootstrap 3,不该升级到 Bootstrap 5 吗?这话没错,但现实是老后台项目里 Bootstrap 3 的存量非常大,尤其是 2015 到 2019 年间上线的管理系统。这类项目的共同点是:业务代码里到处是 .pull-left、.panel、.well 这些 Bootstrap 3 专属类名,升级意味着全量回归测试,成本极高。
这套侧边栏选 Bootstrap 3 技术栈的核心理由就是「零改造成本」。组件本身只用了 Bootstrap 3 的栅格系统(container、row、col-md-*)、面板样式(panel、panel-default)和少量 JS 事件,不依赖 Bootstrap 3 的自带 JS 插件。这意味着如果你在做新项目,直接把组件搬到 Bootstrap 4 或 5 里,只需要调整少量类名,核心的定位和菜单逻辑完全不需要改。
组件本身的体积也很小,CSS 压缩后不到 4KB,JS 不到 2KB,对页面性能的影响可以忽略。相比引入一个完整的 AdminLTE 或类似的模板框架,这套侧边栏更像一个「手术刀」——只解决导航这一个问题,不绑架你的页面结构。
3. 核心实现拆解:三级菜单展开收起与滚动高亮
3.1 HTML 骨架:层级结构与><aside class="sidebar-nav" id="sidebar"> <ul class="sidebar-menu"> <li> <a href="#intro" class="menu-link">1. 项目概述</a> </li> <li class="has-submenu"> <a href="#install" class="menu-link">2. 安装部署</a> <ul class="submenu"> <li><a href="#env">。这样点击菜单不只是切换展开状态,还会触发浏览器原生锚点跳转,滚到对应章节。滚动高亮也是基于这些 id 来判断当前应该高亮哪个菜单项。3.2 CSS 关键样式:固定定位与多级缩进
侧边栏的精华在 CSS,用固定定位把菜单钉在视口左侧,然后通过 padding-left 逐级缩进,形成清晰的视觉层级:
.sidebar-nav { width: 240px; max-height: 100vh; overflow-y: auto; position: fixed; top: 0; left: 0; background: #2c3e50; box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1); z-index: 1000; } .sidebar-menu li a { display: block; padding: 10px 15px; color: #bdc3c7; border-left: 3px solid transparent; } .sidebar-menu .has-submenu > .submenu { display: none; } .sidebar-menu .has-submenu.open > .submenu { display: block; } .sidebar-menu .submenu .submenu { margin-left: 12px; border-left: 1px dashed #34495e; } .sidebar-menu li a.active { color: #fff; background: #34495e; border-left-color: #3498db; }这里有两个比较关键的实现点。第一,position: fixed 加上 max-height: 100vh 和 overflow-y: auto,保证了菜单内容超过一屏时侧边栏自己滚动,而不是把整个页面撑变形。第二,子菜单默认 display: none,只有父节点被加上 .open 类时才显示,这个「类名驱动」的设计比直接改 style.display 更容易调试——在浏览器里手动加一个 .open 类就能肉眼验证样式对不对。
缩进层级用了「嵌套的 ul 加 margin-left」,而不是给每一层写死 padding。这样加第四层菜单样式也会自动对齐,不需要改 CSS。我一般推荐最多三层,超过三层的导航基本就说明信息架构有点问题了。
3.3 JS 逻辑:菜单互斥展开、滚动防抖与锚点高亮
JS 部分是这个组件的灵魂,做了三件事:菜单展开收起、滚动监听高亮、防抖优化。看代码:
(function ($) { $(function () { // 菜单展开收起:点击 has-submenu 的链接时切换 open 类 $('#sidebar').on('click', '.menu-link', function (e) { var $li = $(this).closest('li'); if ($li.hasClass('has-submenu')) { e.preventDefault(); // 阻止锚点跳转,先切菜单状态 $li.toggleClass('open'); // 收起同一层级的兄弟菜单,保证互斥 $li.siblings('.has-submenu').removeClass('open'); } }); // 滚动监听:用防抖降低计算频率 var timer = null; var $sections = $('.sidebar-nav').length ? $('section[id], h2[id], h3[id]') : $(); $(window).on('scroll resize', function () { if (timer) { clearTimeout(timer); } timer = setTimeout(function () { var currentId = null; var scrollTop = $(window).scrollTop() + 80; // 偏移量:留出顶部空间 $sections.each(function () { if ($(this).offset().top <= scrollTop) { currentId = $(this).attr('id'); } }); if (currentId) { $('#sidebar a').removeClass('active'); $('#sidebar a[href="#' + currentId + '"]').addClass('active'); } }, 100); }); }); })(jQuery);展开收起的互斥逻辑用了「兄弟节点互斥」策略:只收平级的兄弟菜单,不动父级和子级。这样用户可以在左侧展开「2. 安装部署」时,再展开「2.2 配置文件」子菜单,不会出现展开一个把整棵树都收起来的情况。阻止默认跳转很重要——如果一个菜单项既是大类又有锚点链接,先处理菜单状态,之后要跳转到第一个子章节,需要再补一段逻辑。
滚动监听的逻辑是「找最后一个滚动位置在视口上方的章节」,把它高亮出来。offset().top <= scrollTop 加上 80px 的偏移量,是为了避免章节标题刚好被页面顶部工具栏挡住时,高亮还停在上一个章节。这里踩过一个坑,见后面「避坑」章。防抖用了 100ms 的 setTimeout,否则滚动时每帧都遍历 DOM 查询 offsetTop,页面会有明显卡顿。
4. 响应式适配与内容区联动:从桌面到移动端的完整方案
4.1 移动端抽屉式侧栏的切换逻辑
桌面端侧边栏固定展示,小屏下继续占 240px 显然不现实,所以这套组件在 768px 断点以下做了「抽屉式」处理——侧边栏默认藏在左侧屏幕外,点击汉堡按钮弹出来,点击遮罩关掉:
$('#sidebarToggle').on('click', function () { $('#sidebar').toggleClass('open'); $('.sidebar-overlay').toggleClass('active'); }); $('.sidebar-overlay').on('click', function () { $('#sidebar').removeClass('open'); $(this).removeClass('active'); });对应 CSS 里的断点处理:
@media (max-width: 767px) { .sidebar-nav { transform: translateX(-100%); transition: transform 0.3s ease; } .sidebar-nav.open { transform: translateX(0); } .sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); z-index: 999; } .sidebar-overlay.active { display: block; } }利用 transform: translateX(-100%) 把菜单移出视口,配合 transition 做滑动动画。这里没有用 Bootstrap 3 自带的 navbar collapse,因为那个组件会把内容压下去而不是覆盖上来,抽屉式的「覆盖」体验在手机上更常见。遮罩层用 z-index: 999 比侧边栏的 1000 低一档,确保菜单盖在遮罩上面,但页面其他内容不会被菜单盖住。
点击遮罩关闭时只移除类名,不直接操作菜单的 open 状态,这样如果菜单已经通过其他方式展开,遮罩关闭时会把「内容区滚动」和「菜单展开」的耦合解掉。万一用户先展开菜单、又滚动了页面,遮罩关闭时不应该顺手把菜单也关了,这里只做自己的清理动作。
4.2 侧边栏高度超限时的滚动策略
菜单项多的时候,240px 宽的侧边栏内部会超出屏幕高度。做法是给 .sidebar-nav 设置 max-height: 100vh 加 overflow-y: auto,让侧边栏自己滚动。这里有两个细节:一是固定定位的 top: 0 会导致菜单贴着屏幕最顶端,如果页面上方有导航条,菜单顶部会被挡住一部分,需要把 top 改成导航条高度;二是浏览器滚动条样式在侧边栏内会出现,影响视觉。
一种常见处理是自定义样式把侧边栏的滚动条改细:
.sidebar-nav::-webkit-scrollbar { width: 4px; } .sidebar-nav::-webkit-scrollbar-thumb { background: #3d566e; border-radius: 2px; }这个不强制,但老项目里经常遇到产品经理嫌默认滚动条丑的情况,顺手加一段总没错。
4.3 内容区的 offset 补偿与锚点跳转修正
桌面端侧边栏固定后,内容区需要左移留出空间。资源包里的实现是在示例页给内容区加了 margin-left: 240px,并利用 Bootstrap 3 的栅格系统让内容宽度自适应。要注意的是,如果侧边栏在移动端被隐藏了,内容区的 margin-left 也应该消失,需要加一条媒体查询:
@media (max-width: 767px) { .main-content { margin-left: 0; padding: 15px; } }另外一个老项目里的典型问题是锚点跳转位置不对——菜单点击后页面确实跳到了对应章节开头的标题处,但标题恰好被顶部工具栏盖住了。解决办法是给内容区的标题加 scroll-margin-top:
h2[id], h3[id] { scroll-margin-top: 20px; }这样浏览器自动让标题停在距离视口顶部 20px 的位置。这里的 20px 和 JS 里的 80px 偏移量要配合好,JS 里的 80px 是「高亮判定」的参考值,scroll-margin-top 是「实际滚动」的目标位置,两边保持一个大概的比例,体验才协调。
5. 常见问题与避坑:Bootstrap 3 侧边栏的五个翻车现场
5.1 侧边栏固定定位后宽度塌掉一半
现象:给 .sidebar-nav 设了 position: fixed,结果菜单宽度突然变成了内容宽度,所有文字挤在一行里来回换行。
原因:position: fixed 是相对视口定位,脱离了文档流。原来靠父容器撑开的宽度在脱流之后「没爹可依」,只能按内容宽度收缩。
解决:给固定定位的侧边栏显式声明 width。如果做了响应式,用 min-width 和 max-width 配合,比如 width: 240px 可以,但别忘了在移动端改成 width: 100%。
5.2>$li.siblings('.has-submenu').removeClass('open');
同理,如果只想收起「当前父节点下的子节点」,先定位父节点再 filter。关键是每次都搞清楚「影响范围」,不要用全局 selector,老项目里到处是地狱级 DOM 结构,selector 写得越精确,翻车概率越低。
5.5 侧边栏被模态框盖住
现象:页面里用 Bootstrap 3 的 modal 弹窗,弹出后侧边栏跑到了弹窗上面挡住内容。
原因:Bootstrap 3 的 modal 默认 z-index: 1050,侧边栏设置了 1000,按理说模态框应该盖住侧边栏。但侧边栏内部某些子元素(比如 .submenu 的边框线)没有设置层级相同时的实际渲染顺序。
解决:实际上问题出在侧边栏的祖先元素建了 stacking context——比如某个父容器无敌的设置了 transform 或 opacity,导致侧边栏的 z-index 只能在局部比较,无法和 modal 竞争。排查办法是在 devtools 里逐个看祖先有没有 transform、filter、perspective。如果有,给侧边栏的祖先去掉这些属性,或者把侧边栏移到离 body 更近的层级再设置高 z-index。
6. 二次开发:把固定菜单改成数据驱动的渲染结构
前面几章快速说了「怎么跑」,这章讲「怎么改」。最实用的改造方向是「数据驱动」——把侧边栏的菜单项抽成配置数组,用一段渲染函数生成 HTML,这样后续维护菜单只需要改数据,不再碰 DOM 结构。
下面是我常用的改造结构,资源包里也附了示例:
var sidebarMenu = [ { title: '1. 项目概述', link: '#intro' }, { title: '2. 安装部署', link: '#install', children: [ { title: '2.1 环境要求', link: '#env' }, { title: '2.2 配置文件', link: '#config', children: [ { title: '2.2.1 数据库连接', link: '#database' }, { title: '2.2.2 缓存设置', link: '#cache' } ] } ] } ]; function renderMenu(items, level) { level = level || 0; var html = '<ul class="' + (level === 0 ? 'sidebar-menu' : 'submenu') + '">'; items.forEach(function (item) { html += '<li' + (item.children ? ' class="has-submenu"' : '') + '>'; html += '<a href="' + item.link + '" class="menu-link">' + item.title + '</a>'; if (item.children) { html += renderMenu(item.children, level + 1); } html += '</li>'; }); html += '</ul>'; return html; } $('#sidebar').html(renderMenu(sidebarMenu));调用 renderMenu 生成完整菜单后,原有的 click 委托和滚动监听逻辑不需要任何改动,因为它们都是基于类名选择器监听的,数据驱动只影响 HTML 的生成方式。唯一要注意的是,深层的 has-submenu 类必须由渲染函数正确输出,否则展开逻辑失效。这个改造完成后,维护菜单改 JS 数组即可。
改造后的验证方法我一般分三步。一是构建验证:改完数据后直接用浏览器打开页面,看菜单层级渲染是否正确,这一步重点检查子菜单的缩进和边框是否连贯。二是交互验证:逐个菜单展开收起,确认互斥逻辑生效,滚动页面看高亮是否按章节切换。三是响应式验证:把窗口缩小到 767px 以下,确认侧边栏收起成抽屉,点击汉堡按钮能打开,点击遮罩能关闭。这三步走完,基本可以保证改造没有引入回归。
做这套组件时印象最深的一次翻车,就是没有验证「菜单互斥」就交出去了,结果用户点开两个菜单后页面布局错乱,被测试追着改了半天。从那以后,我每次改侧边栏相关的代码,都强制走一遍「展开 → 收起 → 互斥 → 滚动高亮」四步自测,再进 git。希望这套 bootstrap3 侧边导航栏的拆解和改造思路能帮到你。
本文还有配套的精品资源,点击获取