ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Angular CDK a11y 无障碍工具包实战指南:键盘导航、焦点管理、实时播报与高对比度样式

Angular CDK a11y 无障碍工具包实战指南:键盘导航、焦点管理、实时播报与高对比度样式 Angular CDK a11y 无障碍工具包实战指南键盘导航、焦点管理、实时播报与高对比度样式【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components本文以 Angular CDK 的 a11y 包为骨架系统讲解其提供的无障碍基础设施ListKeyManager/TreeKeyManager键盘导航、cdkTrapFocus焦点陷阱、InteractivityChecker可交互性检测、LiveAnnouncer屏幕阅读器播报、FocusMonitor焦点来源追踪以及可访问性 Sass 样式工具。读完本文你将掌握这些 API 的调用方式、接口约束与底层实现原理能直接为菜单、列表、树、对话框等组件构建符合 WAI-ARIA 规范的键盘与焦点体验。一、包结构与核心能力总览a11y包位于仓库 src/cdk/a11y由 index.ts 和 public-api.ts 统一导出其目录组织与能力对应如下能力源码位置核心导出列表键盘导航key-managerListKeyManager、FocusKeyManager、ActiveDescendantKeyManager、TreeKeyManager焦点陷阱focus-trapCdkTrapFocus指令、FocusTrap/ConfigurableFocusTrap类可交互性检测interactivity-checkerInteractivityChecker服务屏幕阅读器播报live-announcerLiveAnnouncer服务焦点来源监控focus-monitorFocusMonitor服务及两个监控指令ARIA 描述aria-describerAriaDescriber服务输入方式检测input-modalityInputModalityDetector样式工具_index.scssa11y-visually-hidden、high-contrastmixin高对比度模式high-contrast-modeHighContrastModeDetectorID 生成器id-generator.ts唯一 ID 生成工具二、ListKeyManager列表/菜单/下拉框的键盘导航2.1 基本用法三步接入ListKeyManager用于基于键盘交互管理列表中的活动项适合实现rolemenu或rolelistbox模式的组件。使用它的组件通常做三件事用ViewChildren查询被管理的选项初始化ListKeyManager并传入选项把组件收到的键盘事件转发给ListKeyManager。每个被管理的选项需要实现ListKeyManagerOption接口见 list-key-manager.tsinterface ListKeyManagerOption { disabled?: boolean; getLabel?(): string; }disabled为true时该项会被跳过。默认的跳过谓词_skipPredicateFn (item) item.disabled见 list-key-manager.ts也可以用skipPredicate()自定义跳过逻辑。getLabel()仅在启用 typeahead输入跳转时才必需。ListKeyManager的构造函数支持三种数据源见 list-key-manager.tsnew ListKeyManager(items: QueryListT | T[] | readonly T[]) new ListKeyManager(items: SignalT[], injector: Injector)传入QueryList时管理器会自动订阅其changes事件动态同步新增/移除的选项传入普通数组适用于拿不到QueryList的场景传入Signal时必须同时提供Injector管理器内部通过effect()追踪信号变化。2.2 方向键处理与 wrappingonKeydown(event)是核心入口见 list-key-manager.ts。它按keyCode分派DOWN_ARROW/UP_ARROW处理垂直移动、RIGHT_ARROW/LEFT_ARROW处理水平移动RTL 下左右方向自动互换、HOME/END、PAGE_UP/PAGE_DOWN、TAB触发tabOut流、其余字符键交给 typeahead。导航键命中后会自动调用event.preventDefault()避免页面滚动等默认行为。this.keyManager new FocusKeyManager(...).withWrap();withWrap()开启环绕模式到达列表末尾后继续按方向键会回到另一端见 list-key-manager.ts。底层实现见_setActiveInWrapMode用取模运算(activeIndex delta * i length) % length循环查找下一个未禁用的项。除withWrap外其余常用链式配置方法方法作用默认值withVerticalOrientation(enabled)启用/禁用上下方向键truewithHorizontalOrientation(ltr\|rtl\|null)启用水平导航null禁用nullwithAllowedModifierKeys(keys)允许按住哪些修饰键时仍响应方向键不允许任何修饰键withTypeAhead(debounceInterval)开启输入跳转typeahead需选项实现getLabel()200mswithHomeAndEnd(enabled)Home/End 跳到首/末项falsewithPageUpDown(enabled, delta)PageUp/PageDown 按delta步进跳转delta 10skipPredicate(predicate)自定义跳过谓词跳过disabled项2.3 两种变体FocusKeyManager 与 ActiveDescendantKeyManagerListKeyManager有两个子类对应两种不同的激活语义见 a11y.mdFocusKeyManagerfocus-key-manager.ts——选项直接接收浏览器焦点每个项必须实现FocusableOptioninterface FocusableOption extends ListKeyManagerOption { focus(origin?: FocusOrigin): void; }其setActiveItem在激活新项时调用item.focus(this._origin)让浏览器焦点真正落在该项上见 focus-key-manager.ts。_origin默认是program可通过setFocusOrigin(origin)指定焦点来源让FocusMonitor能正确记录由键盘导航触发的焦点变化。ActiveDescendantKeyManageractivedescendant-key-manager.ts——选项不接收真实焦点而是通过aria-activedescendant标记活动项每个项必须实现Highlightableinterface Highlightable extends ListKeyManagerOption { setActiveStyles(): void; setInactiveStyles(): void; }激活新项时旧项调用setInactiveStyles()清除高亮、新项调用setActiveStyles()应用高亮见 activedescendant-key-manager.ts。此外每个项还必须有绑定到菜单/列表框aria-activedescendant的 ID——这也是 CDK 提供 id-generator.ts 的原因。选择哪种如果选项本身是可聚焦的真实 DOM 元素如button rolemenuitem用FocusKeyManager如果选项是由容器统一管理虚拟焦点的轻量项如 autocomplete 面板用ActiveDescendantKeyManager。2.4 监听活动项变化与资源清理管理器暴露两个可观察对象change: Subjectnumber——活动项索引变化时发出tabOut: Subjectvoid——按下 Tab 键焦点离开列表时发出常用于关闭菜单/弹出层。调用方应在组件销毁时调用destroy()取消订阅并完成流见 list-key-manager.ts。三、TreeKeyManager树形视图的键盘导航TreeKeyManagertree-key-manager.ts用于实现roletree模式的组件它除了移动活动项还会正确处理树的展开/折叠、层级移动和 typeahead见 a11y.md。使用步骤与ListKeyManager类似用ViewChildren查询树节点 → 初始化管理器 → 通过onKeydown转发键盘事件。每个树节点实现TreeKeyManagerItem接口构造函数支持QueryList、普通数组或ObservableT[]三种数据源并接收TreeKeyManagerOptions配置见 tree-key-manager.ts可配置项包括shouldActivationFollowFocus——聚焦时是否同时激活选中该项horizontalOrientation——水平方向ltr/rtl影响左右方向键语义skipPredicate——跳过谓词默认不跳过任何节点含禁用项以符合 ARIA 关于禁用控件可聚焦的建议trackBy——判定节点相等的函数typeAheadDebounceInterval——typeahead 防抖间隔。焦点管理管理器会在键盘交互时自动聚焦合适的项。当活动项变化时通过change属性发出通知组件应据此更新tabindex没有活动项时树本身保留tabindex有活动项时树本身不设tabindex只有活动项对应的 HTML 节点设置tabindex0其余项一律-1。这样保证 Tab 进入树时焦点落在正确的节点上同时避免破坏 tab 键的自然遍历顺序。四、FocusTrap把 Tab 焦点困在指定区域内4.1 cdkTrapFocus 指令cdkTrapFocus指令将 Tab / ShiftTab 的焦点循环约束在元素内部适合模态对话框等需要约束焦点的场景对应 WAI-ARIA 的 dialog-modal 模式。在组件中导入CdkTrapFocus即可使用div classmy-inner-dialog-content cdkTrapFocus !-- Tab 与 Shift Tab 都不会离开这个元素 -- /div注意两点该指令不阻止鼠标交互导致的焦点移出它默认不在初始化时自动捕获焦点因此仅用cdkTrapFocus时页面初次打开不会自动把焦点移入区域。底层实现见 focus-trap.tsFocusTrap类在区域首尾插入两个不可见的锚点元素首锚点获得焦点时跳转到区域内最后一个可 Tab 元素尾锚点获得焦点时跳转到第一个可 Tab 元素从而形成循环其构造函数还接收InteractivityChecker用于兜底判断区域本身是否可聚焦。此外它还通过enabled属性开关锚点的tabIndex支持动态启用/禁用陷阱见 focus-trap.ts。4.2 显式焦点区域cdkFocusRegionStart / cdkFocusRegionEnd / cdkFocusInitial当区域内不止一组焦点时可以用三个 DOM 属性显式声明焦点区域cdkFocusRegionStart与cdkFocusRegionEnd定义焦点循环的起止范围cdkFocusInitial指定区域初始化时接收焦点的元素。a mat-list-item routerLink cdkFocusRegionStartFocus region start/a a mat-list-item routerLinkLink/a a mat-list-item routerLink cdkFocusInitialInitially focused/a a mat-list-item routerLink cdkFocusRegionEndFocus region end/a重要提示如果cdkFocusInitial与cdkTrapFocus一起使用除非同时启用cdkTrapFocusAutoCapture选项否则初始聚焦不会发生——因为CdkTrapFocus默认不在初始化时捕获焦点。五、InteractivityChecker元素可交互性检测InteractivityChecker是用于检测元素可交互性的服务覆盖四种状态见 a11y.mddisabled是否被禁用disabled属性或aria-disabledvisible是否可见tabbable是否可被 Tab 键到达focusable是否可聚焦。它内部被FocusTrap判断兜底聚焦目标、FocusMonitor等模块复用例如焦点陷阱的锚点监听回调中就用this._checker.isFocusable(this._element)判断区域本身能否回退聚焦见 focus-trap.ts。实现位于 interactivity-checker 目录详细 API 参见 API 文档。六、LiveAnnouncer向屏幕阅读器播报消息LiveAnnouncer通过aria-live区域向屏幕阅读器用户播报消息如已保存3 项已选择等。其实现位于 live-announcer.ts服务创建一个隐藏的aria-live元素或复用通过LIVE_ANNOUNCER_ELEMENT_TOKEN注入的自定义元素在 Angular zone 外更新其内容以避免频繁触发变更检测。6.1 基本用法基于注入函数Component({...}) export class MyComponent { private liveAnnouncer inject(LiveAnnouncer); announceMessage() { this.liveAnnouncer.announce(Hey Google); } }announce方法有两个可重载参数见 live-announcer.tspoliteness——off | polite | assertive对应aria-live属性值类型定义见 live-announcer-tokens.tsduration——消息加入 DOM 后清除的毫秒数。方法返回Promisevoid在消息真正写入 DOM 后 resolve。6.2 全局默认配置通过LIVE_ANNOUNCER_DEFAULT_OPTIONS注入令牌见 live-announcer-tokens.ts可以配置全局默认值{ provide: LIVE_ANNOUNCER_DEFAULT_OPTIONS, useValue: { politeness: assertive, duration: 5000 } }LIVE_ANNOUNCER_ELEMENT_TOKEN则允许自定义承载消息的aria-live元素默认为null即由服务自行创建。七、FocusMonitor追踪焦点来源FocusMonitor是可注入服务比监听原生focus/blur事件更强大它能告诉你元素是经由什么方式聚焦的鼠标、键盘、触摸或程序化也支持监听子树焦点。7.1 monitor 方法与 FocusOrigin用monitor(element, checkChildren?)监听元素焦点变化checkChildren传true时只要任一后代获得焦点就视为该元素聚焦默认false。返回一个ObservableFocusOrigin取值含义见 a11y.md 与 focus-monitor.tsFocusOrigin含义mouse鼠标点击聚焦keyboard键盘聚焦touch触摸屏触摸聚焦program程序化调用focus()null元素失焦实现层面FocusMonitor依赖InputModalityDetector记录最近的 mousedown/keydown/touchstart 输入方式并结合窗口聚焦、超时机制把焦点事件归因到对应输入方式见 focus-monitor.ts。检测模式可通过FOCUS_MONITOR_DEFAULT_OPTIONS令牌配置为IMMEDIATE默认只看当前/上一个 tick 的输入或EVENTUAL归因于最后一次对应输入事件见 focus-monitor.ts。7.2 自动附加的 CSS 类被监控的元素聚焦时会自动获得 CSS 类.cdk-focused——元素处于聚焦状态.cdk-${origin}-focused——按焦点来源附加.cdk-mouse-focused、.cdk-keyboard-focused、.cdk-touch-focused或.cdk-program-focused。这些类让开发者可以针对不同输入方式定制视觉反馈如仅键盘导航时显示焦点环、鼠标点击时不显示。7.3 资源释放与 focusVia任何通过monitor监控的元素最终都应调用stopMonitoring(element)取消监控。focusVia(element, origin)允许程序化聚焦时伪造焦点来源若目标元素正在被监控则上报传入的 origin若未被监控则像普通focus()一样聚焦见 a11y.md。7.4 两个便捷指令cdkMonitorElementFocus——等价于对宿主元素调用monitor(el, false)cdkMonitorSubtreeFocus——等价于调用monitor(el, true)。两者都提供Output() cdkFocusChange在FocusOrigin变化时发出新值实现见 focus-monitor.ts 中的CdkMonitorFocus相关指令。示例代码可参考仓库中的focus-monitor-overview、focus-monitor-focus-via与focus-monitor-directives三个官方示例。八、无障碍样式工具Sass mixins8.1 视觉隐藏a11y-visually-hidden屏幕阅读器等辅助技术会跳过display: none、visibility: hidden、opacity: 0、height: 0、width: 0的元素。有时你需要视觉上隐藏元素但保留给辅助技术这时使用a11y-visually-hiddenmixin它会产出.cdk-visually-hidden类use angular/cdk; include cdk.a11y-visually-hidden();div classcustom-checkbox input typecheckbox classcdk-visually-hidden /div典型的应用场景是视觉上隐藏原生 checkbox/radio用自定义样式替代但保留原生控件给辅助技术。mixin 的实现见 _index.scss使用clip: rect(0 0 0 0)配合1px尺寸、绝对定位把元素移出可视区域但保留在可访问性树中并额外处理了 RTL 方向与 Chrome 长文本换行崩溃等边界问题。注意使用 Angular Material 时.cdk-visually-hidden会由其 theming 系统自动包含其他场景需要手动include cdk.a11y-visually-hidden();到全局样式表中。8.2 高对比度模式high-contrast部分操作系统提供高对比度模式High Contrast Modehigh-contrastmixin 让你只为该模式下的用户定义样式其原理是编译为media (forced-colors: ...)媒体查询见 _index.scssuse angular/cdk; button { include cdk.high-contrast { outline: solid 1px; } }mixin 接收可选参数$target指定forced-colors媒体查询的值只能是active或none默认active传入非法值会在编译期抛出 Sass 错误。历史值black-on-white/white-on-black仍被接受但会被归一化为active。九、在真实组件中的组合应用CDK a11y 的这些能力常常组合使用形成一个完整的无障碍组件。以键盘可导航的列表 实时播报 焦点样式为例import {FocusKeyManager, LiveAnnouncer} from angular/cdk/a11y; Component({ selector: app-option-list, template: div rolelistbox (keydown)onKeydown($event) div roleoption *ngForlet opt of options; let i index cdkMonitorElementFocus (cdkFocusChange)onFocusChange(opt, $event) [class.cdk-keyboard-focused]lastOrigin keyboard {{opt.label}} /div /div , }) export class OptionListComponent implements OnInit, OnDestroy { private liveAnnouncer inject(LiveAnnouncer); ViewChildren(RoleOptionDirective) options!: QueryListRoleOptionDirective; keyManager!: FocusKeyManagerRoleOptionDirective; lastOrigin: FocusOrigin | null null; ngOnInit() { this.keyManager new FocusKeyManager(this.options) .withWrap() .withHomeAndEnd() .withTypeAhead(200); } onKeydown(event: KeyboardEvent) { this.keyManager.onKeydown(event); } onFocusChange(opt: any, origin: FocusOrigin) { this.lastOrigin origin; this.liveAnnouncer.announce(${opt.label} focused, polite); } ngOnDestroy() { this.keyManager.destroy(); } }其中RoleOptionDirective只需实现FocusableOptionfocus()方法即可被FocusKeyManager管理。若改用rolemenu的弹出菜单则需在tabOut事件中关闭菜单并把整个面板包上cdkTrapFocus以约束焦点配合AriaDescriberaria-describer还可为元素动态维护aria-describedby描述。十、测试与验证仓库为每个 a11y 模块提供了完整的单元测试是理解行为边界的绝佳材料list-key-manager.spec.ts——验证方向键移动、wrap 环绕、跳过禁用项、typeahead、Home/End/PageUp/PageDown 等tree-key-manager.spec.ts——验证树的展开折叠与焦点管理focus-trap.spec.ts 与 configurable-focus-trap.spec.ts——验证焦点循环、初始聚焦与 autoCapturefocus-monitor.spec.ts——验证各FocusOrigin的归因与 CSS 类附加live-announcer.spec.ts——验证 politeness 属性、消息清除与默认配置。结语CDK a11y 包把 WAI-ARIA 中键盘可操作性焦点管理屏幕阅读器播报高对比度支持等抽象规范落地为可直接注入的 Angular 服务与指令。无论是自研菜单、下拉框、树组件还是为对话框补充焦点约束都可以先看a11y包是否已有现成方案——它的每个模块key-manager、focus-trap、focus-monitor、live-announcer都独立可用且配套的 API 文档如 list-key-manager.md、focus-trap.md、live-announcer.md与单元测试可以帮你快速确认每个 API 的确切行为。【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表