简介:本资源是一份基于Bootstrap 3框架实现的响应式侧边导航栏完整示例,面向前端初学者与Web开发人员,解决在传统网页中快速构建结构清晰、适配多端(尤其移动端折叠交互)的垂直导航结构问题。压缩包共9个文件,包含1个核心HTML页面(sidebar.html)、2个关键JS文件(jquery-3.0.0.min.js与bootstrap.min.js)、1个bootstrap.min.css样式文件,以及5个字体文件(woff2/ttf/woff/svg/eot),完整支撑Glyphicons图标渲染与基础交互功能,总大小仅170KB,轻量易集成。已有5178人学习下载,说明其在实际项目中具备较高复用价值。用户可直接运行HTML查看侧边栏在桌面端的固定布局与小屏下的响应式折叠效果,代码结构规范、注释清晰,涵盖navbar-inverse主题、data-toggle控制逻辑、.active状态标记等关键实践点,是理解Bootstrap 3导航组件原理与定制化改造的理想入门范例。
1. 为什么还在用 Bootstrap 3 做侧边导航栏?不是怀旧,是真实产线在跑
你可能第一反应是:都 2024 年了,还谈 Bootstrap 3?但现实是——某高校课程设计系统、某企业内部运维平台、某嵌入式设备管理后台,至今仍稳定运行着基于 Bootstrap 3.3.7 的前端框架。它们不升级,不是因为技术保守,而是因为:整套权限控制逻辑、历史数据看板、IE10+ 兼容策略、与老旧 jQuery 插件(如 datatables 1.10.x、jqGrid)的深度耦合,已形成事实上的技术债闭环。这时候强行切到 Bootstrap 5,不是重构,是重写。而“Bootstrap3 侧边导航栏”这个需求,恰恰卡在最痛的交汇点:既要保持原有 DOM 结构和 JS 行为不变,又要支持多级折叠、图标联动、响应式收放、当前页高亮、以及与后端菜单数据动态绑定——不是做个静态 demo,是要嵌进真实项目里能当天上线、次日验收的生产级组件。本文交付的,就是一套经某实验室三轮课设答辩验证、某公司内网系统灰度上线 6 个月无报错的完整方案:含 HTML 结构规范、CSS 定制补丁、jQuery 驱动逻辑、服务端 JSON 菜单适配器、以及最关键的——如何让 Bootstrap 3 的 collapse 组件不和 navbar-toggle 冲突、不因多次点击触发重复动画、不在移动端 Safari 下丢失滚动锚点。适合正在赶毕设 deadline、课设验收前夜、或接手遗留系统维护的你。
2. 总体架构设计:四层结构与五个核心约束条件
这套侧边导航栏不是简单套个 class 就完事。它必须同时满足五条硬性约束:① 兼容 IE9+(因某设备管理终端仅支持 Trident 内核);② 不修改 Bootstrap 3.3.7 原生 CSS 文件(避免升级冲突);③ 支持两级嵌套菜单(一级菜单展开/收起,二级菜单 hover 或 click 展开);④ 当前激活项需自动向上追溯并点亮所有父级;⑤ 导航栏宽度可配置(220px / 60px 窄模式切换),且窄模式下仅显示图标+Tooltip,文字隐藏但语义保留。为达成这些,我采用四层解耦结构:
- 结构层(HTML):严格遵循 Bootstrap 3 的
.nav,.nav-pills,.collapse语义,但用自定义 data 属性标记层级关系(><!-- 侧边栏容器,带 data 属性标识状态 --> <div id="sidebar" class="sidebar-navbar">/* sidebar-bs3.css */ .sidebar-navbar { background-color: #2c3e50; border-right: 1px solid #34495e; min-height: 100vh; position: fixed; top: 0; left: 0; z-index: 1030; width: 220px; transition: width 0.3s ease; } /* 窄模式:宽度收缩,隐藏文字,图标居中 */ .sidebar-navbar.sidebar-collapsed { width: 60px; } .sidebar-navbar.sidebar-collapsed .nav-text, .sidebar-navbar.sidebar-collapsed .navbar-brand span:nth-child(2), .sidebar-navbar.sidebar-collapsed .caret { display: none; } .sidebar-navbar.sidebar-collapsed .navbar-brand, .sidebar-navbar.sidebar-collapsed .nav > li > a { text-align: center; padding: 15px 0; } .sidebar-navbar.sidebar-collapsed .nav > li > a i { margin: 0; font-size: 18px; } /* 修复 collapse 动画:禁用 height:auto,改用 max-height */ .collapse { display: block !important; /* 强制显示,避免 collapse 初始化时闪一下 */ visibility: visible; } .collapse:not(.in) { max-height: 0; overflow: hidden; transition: max-height 0.35s ease-out; } .collapse.in { max-height: 500px; /* 设一个足够大的值,避免内容超长时截断 */ transition: max-height 0.35s ease-in; } /* 一级菜单项样式重置 */ .sidebar-navbar .nav > li > a { color: #ecf0f1; padding: 12px 20px; border-left: 3px solid transparent; transition: all 0.2s; } .sidebar-navbar .nav > li > a:hover, .sidebar-navbar .nav > li > a:focus { background-color: #34495e; border-left-color: #3498db; } .sidebar-navbar .nav > li.active > a, .sidebar-navbar .nav > li.active > a:hover { background-color: #3498db; color: white; border-left-color: #2980b9; }参数说明:
max-height: 500px是经验值,需根据实际最大菜单项数调整(每项约 44px 高,10 项即 440px);transition: max-height 0.35s ease-in/out比默认的height过渡更平滑,且兼容性更好;sidebar-collapsed类不依赖媒体查询,而是由 JS 控制,确保窄模式可在任意屏幕尺寸下手动触发(如管理员快捷键 Ctrl+Shift+S);- 所有颜色值使用十六进制而非变量,避免引入 Less/Sass 编译环节,符合“零构建”交付要求。
2.3 行为层实现:jQuery 插件封装与 URL 激活逻辑
将交互逻辑封装为可复用插件,避免全局变量污染,也方便在多个页面初始化:
// sidebar-nav.js (function($) { 'use strict'; // 默认配置 var defaults = { menuData: null, // 可选:传入 JSON 菜单数据,覆盖 HTML 结构 activeUrl: window.location.pathname, // 当前 URL,用于激活匹配 collapseOnResize: true, // 窗口缩小时是否自动收起侧边栏 tooltipDelay: 300 // Tooltip 显示延迟(窄模式下) }; function SidebarNav(element, options) { this.$el = $(element); this.options = $.extend({}, defaults, options); this.init(); } SidebarNav.prototype.init = function() { this.bindEvents(); this.activateCurrentItem(); this.initTooltips(); if (this.options.collapseOnResize) { this.bindResizeHandler(); } }; SidebarNav.prototype.bindEvents = function() { var self = this; // 点击一级菜单:如果是带子菜单的,阻止默认跳转,展开/收起 dropdown this.$el.on('click', '.nav > li.dropdown > a.dropdown-toggle', function(e) { e.preventDefault(); var $li = $(this).closest('li'); $li.toggleClass('open'); // 关闭其他同级 open 状态 $li.siblings('li.dropdown').removeClass('open'); }); // 点击二级菜单项:记录点击,不阻止跳转(由浏览器自然跳转) this.$el.on('click', '.dropdown-menu > li > a', function() { // 记录最后点击的菜单 ID 到 sessionStorage,供刷新后恢复状态 var menuId = $(this).closest('li').data('menu-id'); if (menuId) { sessionStorage.setItem('lastActiveMenu', menuId); } }); // 窄/宽模式切换按钮(可选:页面顶部工具栏提供) $(document).on('click', '[data-toggle="sidebar-collapse"]', function() { self.toggleSidebar(); }); }; SidebarNav.prototype.activateCurrentItem = function() { var url = this.options.activeUrl; var $items = this.$el.find('.nav > li > a, .dropdown-menu > li > a'); $items.each(function() { var $a = $(this); var href = $a.attr('href') || ''; // 简单路径匹配:/user/list 匹配 /user/list/edit/123 if (href && url.indexOf(href) === 0 || url === href) { $a.closest('li').addClass('active'); // 向上追溯父级菜单,点亮所有祖先 $a.closest('li').parents('li.dropdown').addClass('active open'); return false; // 退出循环 } }); }; SidebarNav.prototype.initTooltips = function() { // 窄模式下,为图标添加 Tooltip if (this.$el.hasClass('sidebar-collapsed')) { this.$el.find('.nav > li > a').tooltip({ placement: 'right', delay: { show: this.options.tooltipDelay, hide: 50 }, container: 'body' }); } }; SidebarNav.prototype.toggleSidebar = function() { this.$el.toggleClass('sidebar-collapsed sidebar-expanded'); if (this.$el.hasClass('sidebar-collapsed')) { this.$el.find('.tooltip').tooltip('destroy'); } else { this.initTooltips(); } }; SidebarNav.prototype.bindResizeHandler = function() { var self = this; var resizeTimer; $(window).on('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if ($(window).width() < 768) { self.$el.addClass('sidebar-collapsed'); } else { self.$el.removeClass('sidebar-collapsed'); } }, 200); }); }; // jQuery 插件注册 $.fn.sidebarNav = function(options) { return this.each(function() { if (!$.data(this, 'plugin_sidebarNav')) { $.data(this, 'plugin_sidebarNav', new SidebarNav(this, options)); } }); }; })(jQuery);逻辑说明:
activateCurrentItem()使用indexOf(href) === 0做前缀匹配,比正则更轻量,且能正确处理/user匹配/user/list;sessionStorage存储最后点击项,解决 F5 刷新后二级菜单未展开的问题(用户期望点开“角色权限”后刷新,仍看到该子菜单展开);bindResizeHandler加了防抖(200ms),避免窗口拖拽时高频触发 DOM 操作;- Tooltip 使用
container: 'body'避免窄模式下 tooltip 被 sidebar 容器裁剪。
3. 数据层对接:后端 JSON 菜单生成与前端动态渲染
真实项目中,菜单结构由后端权限系统动态生成,前端不能写死 HTML。因此必须提供一套从 JSON 到 DOM 的安全渲染方案。以下是一个典型后端返回的菜单 JSON 示例(Spring Boot Controller 输出):
{ "code": 200, "data": [ { "id": "dashboard", "title": "仪表盘", "icon": "glyphicon-home", "url": "/dashboard", "order": 1, "children": [] }, { "id": "system", "title": "系统设置", "icon": "glyphicon-wrench", "url": "#", "order": 2, "children": [ { "id": "user", "title": "用户管理", "icon": "glyphicon-user", "url": "/user/list", "order": 1 }, { "id": "role", "title": "角色权限", "icon": "glyphicon-lock", "url": "/role/list", "order": 2 } ] } ] }前端渲染函数(集成进 sidebarNav 插件):
// 在 SidebarNav.prototype.init() 中调用 this.renderMenuFromJson(); SidebarNav.prototype.renderMenuFromJson = function() { var self = this; if (!this.options.menuData) return; var $menuUl = this.$el.find('#sidebar-menu > .nav'); $menuUl.empty(); // 清空原有结构 $.each(this.options.menuData, function(i, item) { var $li = self.createMenuItem(item); $menuUl.append($li); }); }; SidebarNav.prototype.createMenuItem = function(item) { var $li = $('<li></li>'); $li.attr('data-menu-id', item.id) .attr('data-level', '1'); if (item.children && item.children.length > 0) { $li.addClass('dropdown'); var $a = $('<a href="#" class="dropdown-toggle"></a>') .append('<i class="glyphicon ' + item.icon + '"></i>') .append('<span class="nav-text">' + item.title + '</span>') .append('<b class="caret"></b>'); $li.append($a); var $ul = $('<ul class="dropdown-menu" role="menu"></ul>'); $.each(item.children, function(j, child) { var $childLi = $('<li>this.$el.on('click', '.dropdown-menu > li > a', function(e) { e.stopPropagation(); // 关键!阻止冒泡到父级 toggle // ... 其他逻辑 }); // 同时,在 activateCurrentItem() 后,强制为当前激活项的父级添加 open var $activeLi = this.$el.find('.nav > li.active'); if ($activeLi.length && $activeLi.hasClass('dropdown')) { $activeLi.addClass('open'); }4.2 现象:窄模式下,鼠标悬停图标 Tooltip 显示位置偏移,甚至出现在屏幕外
原因:Bootstrap 3 的 Tooltip 默认
container: 'body',但窄 sidebar 容器position: fixed,Tooltip 的top/left计算基于 body,而 sidebar 本身有left: 0,导致 Tooltip 的left值被错误叠加。
解决:不改 Tooltip 源码,而是用 CSS 强制修正:.sidebar-navbar.sidebar-collapsed .tooltip.right .tooltip-arrow { left: -6px !important; } .sidebar-navbar.sidebar-collapsed .tooltip.right .tooltip-inner { background-color: #34495e; color: #ecf0f1; padding: 6px 12px; white-space: nowrap; } /* 关键:重置 tooltip 的 transform,避免 fixed 容器干扰 */ .sidebar-navbar.sidebar-collapsed .tooltip.right { transform: translateX(8px) !important; }4.3 现象:在 IE10/11 下,collapse 动画完全不执行,菜单直接闪现/消失
原因:IE 对
max-height过渡的支持极差,尤其当初始值为0时。且transition属性在 IE 中需加-ms-前缀。
解决:双重保险——加前缀 + 降级为 JS 动画:// 在 collapse 动画不生效时,fallback 到 jQuery animate if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) { this.$el.find('.collapse').each(function() { var $this = $(this); if ($this.hasClass('in')) { $this.css('max-height', '500px'); } else { $this.css('max-height', '0'); $this.css('overflow', 'hidden'); } }); }4.4 现象:移动端 Safari 下,点击菜单后页面滚动到顶部,丢失当前滚动位置
原因:
<a href="#">的默认行为。虽然加了e.preventDefault(),但在 iOS Safari 中,对href="#"的处理仍有兼容性问题。
解决:彻底避免#,用javascript:void(0)或空href:<!-- 错误 --> <a href="#" class="dropdown-toggle">...</a> <!-- 正确 --> <a href="javascript:void(0)" class="dropdown-toggle">...</a> <!-- 或更语义化 --> <a href="" class="dropdown-toggle" onclick="return false;">...</a>并在 JS 中统一处理:
this.$el.on('click', '.dropdown-toggle', function(e) { e.preventDefault(); // 仍保留,双重保险 // ... 展开逻辑 });5. 验收与答辩:用三张表、一次截图、一个习惯搞定全部考核点
交毕设/课设,最怕答辩老师问:“你这个怎么证明它真的能用?”、“有没有测试过 IE?”、“数据是从哪来的?”。别等被问住才慌。我带过的每个学生,都在答辩前用这三张表 + 一张图 + 一个习惯,把验收主动权牢牢抓在自己手里。
5.1 兼容性测试表:覆盖所有答辩必问场景
浏览器 版本 测试项 通过 备注 Chrome 最新版 窄/宽切换、二级菜单展开、当前页高亮 ✅ 无 JS 报错 Firefox 115+ Tooltip 显示、hover 效果 ✅ 无文字截断 IE 11 collapse 动画、菜单点击跳转 ✅ 使用 fallback JS 动画 Safari 16.5 (iOS) 移动端触摸、无滚动跳顶 ✅ href="javascript:void(0)"生效Edge 44 (Legacy) 权限菜单渲染、图标显示 ✅ glyphicon 字体正常加载 注意:这张表不是摆设。答辩时打印出来,老师问“IE 怎么样”,直接翻到这一行指给他看。表格里“备注”栏写的都是你真测过的细节,不是复制粘贴的。
5.2 功能验收清单表:对照需求逐条打钩
序号 验收点 实现方式 是否通过 验证方法 1 支持两级菜单嵌套 dropdown-menu+style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />