R47第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

可访问组件实践

可访问组件实践 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完你能实现一个键盘可完整操作、角色与状态标注正确的标签页组件:role 命名、方向键移动、roving tabindex、aria-selected、可见焦点;并掌握一套「只用键盘」的验证流程。

问题场景

课程详情页的标签页(简介 / 章节 / 评价)第一版是三个 div 加 onClick:鼠标点了就切;键盘用户按 Tab 却只能「绕过」它们——div 不在焦点序列里;屏幕阅读器读出「文本、文本、文本」,不知道这是标签页;焦点 outline 又因为「不好看」被全局 CSS 删了。三类用户同时被挡在门外。

核心解释

按 WAI-ARIA 的 Tabs 模式补齐四层。

一、角色与状态标注——告诉辅助技术「这是什么、现在什么状态」:

<div role="tablist" aria-label="课程内容">
  {tabs.map((t) => (
    <button key={t.id} role="tab" id={"tab-" + t.id}
      aria-selected={active === t.id}
      aria-controls={"panel-" + t.id}
      tabIndex={active === t.id ? 0 : -1}>
      {t.label}
    </button>
  ))}
</div>
<div role="tabpanel" id={"panel-" + active} aria-labelledby={"tab-" + active}>
  {content}
</div>

底座用 button 而不是 div:button 天然可聚焦、可 Enter/Space 激活。无障碍第一原则是优先用语义化元素,不够再补 ARIA,而不是给 div 贴满属性。

二、roving tabindex:tablist 里只有当前活动 tab 的 tabIndex 是 0,其余是 -1——Tab 键「进入标签组」只停一次,组内用方向键移动,避免按 N 次才能穿过。

三、键盘行为:左右方向键移动并循环,Home/End 跳两端;「移动即切换」(自动激活)或「Enter 确认」(手动激活)二选一并保持一致:

function onKeyDown(e, id) {
  const idx = tabs.findIndex((t) => t.id === id);
  if (e.key === "ArrowRight") { e.preventDefault(); activate(tabs[(idx + 1) % tabs.length].id); }
  if (e.key === "ArrowLeft")  { e.preventDefault(); activate(tabs[(idx - 1 + tabs.length) % tabs.length].id); }
}

四、可见焦点:绝不裸删 outline;要改样式就给 :focus-visible 换一个更醒目的替代(粗外框、加粗)。焦点看得见是键盘可用的前提。

验证流程全程不碰鼠标:Tab 进入 → 方向键逐个切换 → 每一步焦点可见、面板内容跟随 → Tab 离开,焦点落到面板内容。

示例拆解

实验区是补齐后的三标签课程页:

  • 语义结构:role=tablist/tab/tabpanel,aria-selected 随激活变化,tab 与 panel 用 id 互相关联;
  • roving tabindex:Tab 只进入一次,方向键在标签间循环;
  • 焦点环:聚焦的标签显示明显的琥珀色外框(模拟 :focus-visible 的替代样式);
  • 面板内容随激活即时切换;
  • 验证:按提示「只用键盘」走完整旅程,再用 DevTools 的无障碍面板查看角色树——读出的应是「标签页」结构而不是三个文本。

动手任务

打开本课实验区:

  1. 加 Home / End 键:焦点与选中跳到第一个 / 最后一个标签。
  2. 把「移动即切换」改成「Enter 确认切换」的手动激活模式:方向键只移焦点,面板暂不切换。
  3. 用 DevTools 的无障碍(Accessibility)面板检查 tablist 角色,确认读出的结构与语义一致。

常见错误

  • div + onClick 充当按钮:不在焦点序列、读不出角色;用 button,或补 role + tabIndex + 键盘处理。
  • 每个 tab 都 tabIndex=0:键盘用户要按 N 次才能穿过标签组;用 roving tabindex。
  • 删掉 outline 却不给替代:焦点不可见等于对键盘用户隐身;用 :focus-visible 提供更醒目的样式。
  • aria-selected 标到面板上:它是 tab 的状态;面板用 aria-labelledby 关联对应标签。

检查与复盘

自查清单:

  • 不碰鼠标完成「进入、切换、离开」三个环节
  • 角色与状态标注齐全,无障碍树读出的是标签页结构
  • 焦点全程可见,没有裸删 outline

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

可访问性:下列搜索框写法中,屏幕阅读器用户能可靠获知「这是搜索框」的是?

Q2多选中等(多选,全部选对才算正确)

下列哪些做法真正改善了可访问性?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...