禁用状态必须同时设置tabindex="-1"和aria-disabled="true",仅加disabled类无法阻止键盘聚焦和屏幕阅读器识别;当前页必须用aria-current="page"标识语义,active类仅控制样式。

禁用状态必须同时加 disabled 和 aria-disabled="true"
只给 <li> 加 disabled 类,按钮视觉上灰了,但键盘用户仍能 Tab 进去、屏幕阅读器也认为它可操作——这违反 WCAG 2.1。Bootstrap 的 disabled 类只负责样式(比如 opacity: .5),不改语义。
正确做法是:在 <li class="page-item disabled"> 内部的 <a></a> 或 <button></button> 上,同时设置:
-
tabindex="-1"阻止键盘聚焦 -
aria-disabled="true"告诉辅助技术这是禁用态 - 如果用
<a href="#"></a>,务必加event.preventDefault(),否则点击会跳到页面顶部
别用 pointer-events: none 代替,它会让焦点和读屏完全失效,且 iOS Safari 下可能穿透点击到底层元素。
active 类只控制样式,aria-current="page" 才是语义关键
给当前页 <li class="page-item active"> 加 active,只是让背景变蓝、字体加粗;对搜索引擎和读屏软件毫无意义。真正告诉浏览器“这是当前页”的,是 aria-current="page" 属性,必须手动加在当前页的链接或按钮上。
常见错误:
- 只加
active,漏掉aria-current→ 可访问性检测工具直接报错 - 动态切换页码时,忘了移除上一页的
aria-current→ 出现多个“当前页”,逻辑混乱 - 用
<span></span>替代<a></a>显示当前页(推荐做法),但没加aria-current="page"→ 语义丢失
示例正确结构:<li class="page-item"><span class="page-link" aria-current="page">3</span></li>
禁用/激活状态不能只靠 CSS 类切换
很多人用 JS 动态切换 disabled 或 active 类,却忽略背后的 DOM 属性同步。比如点击“上一页”后禁用该按钮,只执行 el.classList.add("disabled") 是不够的。
必须同步操作:
- 禁用时:
el.setAttribute("aria-disabled", "true")+el.setAttribute("tabindex", "-1") - 启用时:
el.removeAttribute("aria-disabled")+el.setAttribute("tabindex", "0") - 切换当前页时:
prevLink.removeAttribute("aria-current"),再给新链接加aria-current="page"
否则,键盘用户按 Tab 仍会停在已“视觉禁用”的按钮上,或者读屏连续播报两个“当前页”。
移动端点不准?不是 pagination-sm 的锅,是热区太小
pagination-sm 确实会让页码变小,但它本身不破坏可点击性。iOS Safari 下点不准,大概率是因为 .page-link 的 padding 太小(默认仅 0.375rem 0.75rem),导致触摸热区不足 44×44px —— 这是苹果人机接口指南的最低要求。
解决方式很简单:
- 用 Bootstrap utility 类补足:
py-2 px-3(确保垂直 ≥ 32px,水平 ≥ 44px) - 或加自定义 CSS:
.page-link { min-width: 44px; },避免影响其他尺寸变体 - 别强行放大字体(如
fs-5)来凑热区,会破坏排版节奏
事情说清了就结束。分页的禁用与高亮,表面是加两个类,背后是语义、焦点、触控、读屏四条线必须拧成一股劲。漏掉任何一环,用户就卡在某一页动不了。











