
你肯定遇到过这种场景一个列表页数据一多底部那排“1 2 3 4 5”的链接要么样式丑得没法看要么点半天没反应另一个场景更常见导航条在电脑上显示正常一缩到手机菜单死活不展开点那个小汉堡图标跟点墙一样。这两个问题十有八九是Bootstrap导航条navbar和分页导航pagination的细节没吃透。我这些年用Bootstrap帮公司做后台系统、官网、运营活动页几乎每个项目都绕不开这两个组件。说句题外话如果你是被“Bootstrap方法”“SPSS怎么做Bootstrap中介分析”这些词带进来的先别急着走——你说的Bootstrap是统计学里的自助抽样法和前端框架只是同名。今天这篇只聊网页上的Bootstrap导航条和分页导航。本文会把基础写法、定制技巧、常见坑一次说清适合刚接触Bootstrap没几天的新手也适合写了很多页面但一直靠复制文档、从来没深究过原理的老手。1. 先搞清楚Bootstrap导航条和分页导航到底是什么1.1 同名不同命前端Bootstrap和统计Bootstrap方法很多人在搜索引擎里搜Bootstrap页面会混进来大量“SPSS bootstrap中介分析”“Bootstrap方法检验”之类的词。那不是网页框架而是统计学里的一种重抽样方法常用于估计标准误、置信区间或者做中介效应分析。前端Bootstrap是由Twitter团队开源的一套CSS/JS组件库核心作用就是让不会写复杂CSS的人也能快速做出整洁、响应式的页面。这两者唯一的共同点就是名字都叫Bootstrap翻译过来是“引导、自举”的意思。统计里的“自助法”指的是通过反复抽样来逼近真实分布前端里的“自举”指的是框架自带样式和交互你不用从零开始写。今天说清楚这件事能避免很多人在查资料的时候走错门。1.2 导航条Navbar和分页Pagination各自解决什么问题导航条是一个网站的骨架。用户在页面上能不能快速找到“首页”“产品”“关于我们”全靠导航条。一个设计糟糕的导航条轻则让用户多点两次鼠标重则让用户迷路直接关掉页面。Bootstrap的navbar组件把这一整套架构封装好了包括品牌标识、菜单链接、下拉面板、搜索框、汉堡按钮而且能自动适配不同屏幕宽度。分页导航解决的是“数据展示不下”的问题。当列表有几十上百条记录时全部堆在一个页面既不美观也影响性能。分页把数据切成固定数量的小块用户通过翻页查看。Bootstrap的pagination组件把页码排成一排支持激活态、禁用态、大小尺寸、对齐方式还能和前端JS或后端接口无缝配合。这两个组件看起来简单但实际项目里十有八九会被改得面目全非。我见过有人为了改一个导航条颜色写了几十行覆盖样式最后越改越乱也有人把分页做成纯静态标签点击之后毫无反应。所以后面几节我会把从零到一、从静态到动态的完整思路都讲一遍。2. 导航条Navbar从入门到定制2.1 5分钟搭出一个能用的导航条先给一个最标准的Bootstrap 5导航条别嫌代码多每一个类名都有它的用处。这里用CDN方式引入本地项目一样能用。!doctype html html langzh-CN head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 titleBootstrap导航条示例/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.3/dist/css/bootstrap.min.css relstylesheet /head body nav classnavbar navbar-expand-lg navbar-light bg-light div classcontainer-fluid a classnavbar-brand href#我的网站/a button classnavbar-toggler typebutton>.navbar { --bs-navbar-color: #2c3e50; --bs-navbar-hover-color: #1a73e8; --bs-navbar-active-color: #1a73e8; --bs-navbar-brand-color: #1a73e8; --bs-navbar-brand-hover-color: #1558b0; }这样改的好处是不用去抠每个.nav-link的样式语义清晰后续维护一眼就能看懂。说句实话很多人对Bootstrap有偏见觉得它“千篇一律”其实是你没用到变量机制。2.3 导航条里塞这些东西下拉、搜索框、文字、图标导航条不只是放几个链接。我做过一个后台系统导航条右侧要放搜索框、用户头像、通知图标还要有下拉菜单放“个人中心”“退出登录”光靠基础链接是不够的。下拉菜单的写法其实是独立的dropdown组件但可以无缝塞进导航条ul classnavbar-nav me-auto mb-2 mb-lg-0 li classnav-item dropdown a classnav-link dropdown-toggle href# idnavbarDropdown rolebutton>form classd-flex rolesearch input classform-control me-2 typesearch placeholder搜点什么 aria-label搜索 button classbtn btn-outline-success typesubmit搜索/button /formd-flex是Bootstrap的flex工具类让输入框和按钮横向排列。如果你希望它靠右可以把它放在.collapse容器里同时给前面的菜单加上me-auto菜单就会被推到左侧搜索框留在右侧。这种“菜单左、工具右”的布局在真实项目里出现频率极高。如果你要放一段不需要交互的说明文字可以用navbar-text。很多博客导航条上会写“欢迎来访”之类的文字这个类名专门干这个。它的好处是会自动继承导航条与前后元素的垂直居中样式不用手动调line-height。2.4 固定导航条与遮挡问题的处理细节导航条常见的三种固定方式fixed-top、fixed-bottom、sticky-top。fixed-top导航条固定在视口顶部不随页面滚动脱离文档流。fixed-bottom固定在底部同样脱离文档流。sticky-top粘性定位初始是普通的流内元素滚过它时才固定在顶部不脱离文档流。我强烈建议能用sticky-top就别用fixed-top。为什么因为fixed-top脱离文档流页面正文顶上去了一截需要你自己给body或main加padding-top。而导航条的高度并不是固定的它包含的链接、下拉、搜索框可能会换行高度从50px到70px不等硬编码一个padding-top很容易在某个断点上翻车。如果你确实要用fixed-top最简单的方案是给body加一个和导航条高度差不多的paddingbody { padding-top: 70px; }但更好的方案是直接改成sticky-topnav classnavbar navbar-expand-lg navbar-light bg-light sticky-top这样既不用考虑高度也不需要额外padding页面滚动时导航条会自动吸顶。另外固定导航条后页面里如果有锚点链接比如“点击跳转到某个板块”导航条会遮住锚点目标的上半部分。解决办法是在CSS里设置滚动偏移html { scroll-padding-top: 80px; }这个值稍微大于导航条高度即可。这一行CSS算是固定导航条页面的必备兜底方案。3. 分页导航Pagination的正确打开方式3.1 基础分页结构active、disabled和尺寸变体分页导航在Bootstrap里长这样nav aria-label列表分页 ul classpagination li classpage-item disabled a classpage-link href# tabindex-1 aria-disabledtrue上一页/a /li li classpage-item active aria-currentpage a classpage-link href#1/a /li li classpage-item a classpage-link href#2/a /li li classpage-item a classpage-link href#3/a /li li classpage-item a classpage-link href#下一页/a /li /ul /nav.pagination是整个分页组件的根容器所有页码都要包在.page-item里每个链接或按钮用.page-link。这里的结构看起来简单但有一个容易忽略的点nav标签上的aria-label和激活页上的aria-currentpage都是无障碍语义的一部分。屏幕阅读器能靠这些属性告诉视障用户“你在第几页”这对网站合规性是有帮助的不要删。尺寸变体有pagination-lg和pagination-sm。后台管理系统里的分页一般比较密集用默认尺寸就好如果是官网的博客底部可以试试pagination-lg视觉上更有存在感。3.2 状态管理激活态和禁用态别写错激活态对应当前页写法是给li加active同时在a上保留正常的高亮样式。代码里那行aria-currentpage是辅助技术识别用的千万别省。禁用态对应不可点击的按钮比如第一页时“上一页”应该禁用。写法是给li加disabled并且给a加上tabindex-1和aria-disabledtrue。这里有个细节Bootstrap只是把链接的默认点击行为屏蔽了但:hover样式可能还在。如果你不希望禁用态有任何点击反馈可以在覆盖样式里加.page-item.disabled .page-link { pointer-events: none; }但要注意pointer-events: none会让链接完全不可点击同时也可能影响无障碍工具的操作。最好还是配合tabindex-1一起用。还有更普遍的一个坑分页链接的href#是占位写法。如果用户点击时JS没有阻止默认行为页面直接跳到顶部非常烦人。正确做法是在JS事件回调里调用event.preventDefault()这个后面动态渲染部分会演示。3.3 对齐、箭头和省略号分页的实用变体默认分页是左对齐的。因为分页组件是普通的flex容器所以你可以直接用flex工具类改对齐方式。居中可以用justify-content-center右对齐用justify-content-end。比如把下面这段放在页面底部nav aria-label列表分页 classd-flex justify-content-center ul classpagination ... /ul /nav这种写法比给ul单独加margin要省事得多因为分页本身就是flex布局父级用flex工具类就能控制对齐。实际项目中页码特别多的时候不可能把所有页都显示出来。常见的做法是用省略号代替中间一大段。Bootstrap里省略号就是用禁用态的li包裹一个spanli classpage-item disabled span classpage-link…/span /li上一页和下一页的图标也可以换成箭头符号。常见写法li classpage-item a classpage-link href# aria-label上一页 span aria-hiddentruelaquo;/span /a /lilaquo;是左双角引号«raquo;是右双角引号»。很多网站喜欢用这两个字符代替“上一页”“下一页”的文字视觉上更干净。但要注意如果你的用户群体年龄偏大还是保留“上一页”“下一页”文字更友好。3.4 分页和列表数据怎么联动动态渲染与事件委托大多数真实项目里分页页码不是写死在HTML里的而是根据后端返回的总页数动态生成。这里我给一个我自己常用的renderPagination函数生成带省略号的分页结构同时用事件委托绑定点击。function renderPagination(totalPages, currentPage, containerId, onPageChange) { const container document.getElementById(containerId); if (!container) return; let html ; // 上一页 html li classpage-item ${currentPage 1 ? disabled : } a classpage-link href#>renderPagination(50, 3, paginationContainer, function (page) { fetchData(page); });然后写一个fetchData去请求对应页码的数据刷新列表。这就是前后端分离项目中最常见的一种分页交互方案。4. 一个导航条加分页的完整页面案例4.1 页面需求与整体结构说了这么多理论不如直接做一个能跑的例子。假设要做的是一个“文章列表页”页面头部有导航条下面展示文章列表底部有分页。点击页码时列表内容跟着变化。这个场景几乎覆盖了后台列表、官网新闻、博客分页的所有核心痛点。整体HTML结构可以这样规划body nav classnavbar navbar-expand-lg navbar-dark bg-dark sticky-top.../nav main classcontainer mt-4 h1最新文章/h1 div idarticleList classlist-group mb-4.../div nav aria-label分页 classd-flex justify-content-center ul idpaginationContainer classpagination/ul /nav /main script src...bootstrap.bundle.min.js/script script srcapp.js/script /body这里用sticky-top做吸顶导航条省掉fixed-top的padding问题。文章列表区域是一个空的div由JS填充。分页区域直接放一个ul由前面的renderPagination函数渲染。4.2 导航条部分实现导航条用深色主题。因为这是示例我只写两组菜单和一个品牌区但足以覆盖前面讲过的点nav classnavbar navbar-expand-lg navbar-dark bg-dark sticky-top div classcontainer a classnavbar-brand href#前端笔记/a button classnavbar-toggler typebutton>const allArticles []; for (let i 1; i 120; i) { allArticles.push({ id: i, title: 这是第 i 篇文章 }); } const pageSize 10; let currentPage 1; function fetchData(page) { currentPage page; const start (page - 1) * pageSize; const end start pageSize; const data allArticles.slice(start, end); const html data.map(function (item) { return a href# classlist-group-item list-group-item-action strong item.title /strong span classtext-muted float-end2024-01-01/span /a; }).join(); document.getElementById(articleList).innerHTML html; const totalPages Math.ceil(allArticles.length / pageSize); renderPagination(totalPages, page, paginationContainer, fetchData); } fetchData(1);这个逻辑很直白fetchData根据页码切出对应数据更新列表然后渲染分页。分页的点击回调又调用fetchData形成闭环。这里要提醒一句如果切到第5页后列表只剩第5页数据了但分页组件也会重新生成当前页高亮会保持在5不会串页。4.4 定制样式与细节打磨有些细节需要靠CSS覆盖。比如把分页激活态改成不是默认的蓝色比如品牌专属的紫红色。再比如给导航条加一条很细的阴影让它在吸顶时和背景有区分。.navbar { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .pagination .page-item.active .page-link { background-color: #6f42c1; border-color: #6f42c1; } .pagination .page-link { color: #6f42c1; } .list-group-item:hover { background-color: #f8f9fa; }这些覆盖写法的优先级应该足够。如果遇到加不上的情况可以检查一下是不是自己的样式表在bootstrap.css之前引入了。CSS的层叠规则很简单相同优先级时后加载的覆盖先加载的。所以你的自定义样式文件一定要放在Bootstrap的样式之后。另外如果你在做后台项目分页数量可能很大比如几百页。这种情况下与其把所有页码全部展示不如只显示当前页附近5页然后加一个“跳页”输入框。这个扩展可以先预留一个“跳页”按钮在分页右侧后续有时间再做。大多数情况下renderPagination函数的省略号策略已经足够好用。5. 常见问题与排查技巧实录5.1 导航条折叠不生效先查这三处导航条在手机上点了汉堡按钮没反应是最常见的问题。我整理了一个排查顺序第一确认引入了bootstrap.bundle.min.js并且放在/body前面。只引CSS不引JS折叠功能就没了。第二检查汉堡按钮的>html { scroll-padding-top: 80px; }这一行设置会让浏览器在锚点定位时自动留出顶部空间。如果你还在用老的body { padding-top: 70px; }也别忘了同时处理锚点偏移。我建议新项目直接使用sticky-top然后配上一行scroll-padding-top安稳很多。5.3 分页点击没反应或总是跳回顶部分页点击没反应通常有三个原因第一没有引入Bootstrap的JS但这只会影响带下拉、折叠等交互的组件分页本身是纯CSS点击行为得靠你自己绑定。第二动态生成的HTML里事件绑定的时机不对。如果你在页面初始化时给分页链接绑定了事件后来用户切换页码后innerHTML被替换新生成的链接没有事件点击自然没反应。解决办法就是事件委托像我前面的renderPagination函数那样只在容器上监听一次。第三点击后页面跳回顶部因为href#的默认行为没有被阻止。在回调里加上e.preventDefault()即可。如果你用的是jQuery也容易掉进一个坑用$(.page-link).click()给动态元素绑定事件但新元素渲染后这个绑定失效。jQuery的解决方案是$(document).on(click, .page-link, function(){...})原理和原生事件委托一样。不过既然都用Bootstrap 5了我建议能不用jQuery就不用原生JS的事件委托同样简洁。5.4 样式冲突与版本坑Bootstrap 4和5的差异很大网上搜到的很多老代码是Bootstrap 4的。比如Bootstrap 4的分页类名是page-item和page-link这一点和5一致但导航条里的很多内部类改了。最显著的是Bootstrap 4用>