无障碍性 Accessibility

a11y 概览

a11y 包提供了许多提高无障碍性(可访问性)的工具,如下所述。

The a11y package provides a number of tools to improve accessibility, described below.

ListKeyManager(列表键盘管理器)

ListKeyManager

ListKeyManager 可以通过键盘交互来管理条目列表中的激活选项。主要针对带有 role="menu"role="listbox" 的组件。

ListKeyManager manages the active option in a list of items based on keyboard interaction. Intended to be used with components that correspond to a role="menu" or role="listbox" pattern.

那些使用 ListKeyManager 的组件通常要做三件事:

Any component that uses a ListKeyManager will generally do three things:

  • 为要管理的条目创建一个 @ViewChildren 查询。

    Create a @ViewChildren query for the options being managed.

  • 初始化 ListKeyManager,并传入这些选项。

    Initialize the ListKeyManager, passing in the options.

  • 把键盘事件从被管理的组件转发到 ListKeyManager

    Forward keyboard events from the managed component to the ListKeyManager.

每个选项都应该实现 ListKeyManagerOption 接口:

Each option should implement the ListKeyManagerOption interface:

interface ListKeyManagerOption {
  disabled?: boolean;
  getLabel?(): string;
}

可以通过 withWrap 方法对 withWrap 选项进行导航

Navigation through options can be made to wrap via the withWrap method

this.keyManager = new FocusKeyManager(...).withWrap();

ListKeyManager 有两种变体形式:FocusKeyManagerActiveDescendantKeyManager

There are two varieties of ListKeyManager, FocusKeyManager and ActiveDescendantKeyManager.

当这些选项能直接接收浏览器焦点时使用。每个受管理的选项都必须实现 FocusableOption 接口:

Used when options will directly receive browser focus. Each item managed must implement the FocusableOption interface:

interface FocusableOption extends ListKeyManagerOption {
  focus(): void;
}

当这些选项由 aria-activedescendant 标为激活时使用。每个受管理的选项都必须实现 Highlightable 接口:

Used when options will be marked as active via aria-activedescendant. Each item managed must implement the Highlightable interface:

interface Highlightable extends ListKeyManagerOption {
  setActiveStyles(): void;
  setInactiveStyles(): void;
}

每个条目都必须有一个 ID,用于绑定到列表框或菜单的 aria-activedescendant

Each item must also have an ID bound to the listbox's or menu's aria-activedescendant.

FocusTrap(焦点陷阱)

FocusTrap

cdkTrapFocus 指令用于捕获一个元素中的 Tab 键焦点。这可以用来创建模态对话框等组件的无障碍体验,这时候必须限制焦点的移动。

The cdkTrapFocus directive traps Tab key focus within an element. This is intended to be used to create accessible experience for components like modal dialogs, where focus must be constrained.

该指令声明在 A11yModule 中。

This directive is declared in A11yModule.

<div class="my-inner-dialog-content" cdkTrapFocus>
  <!-- Tab and Shift + Tab will not leave this element. -->
</div>

该指令不会阻止焦点因鼠标交互而移出陷阱区域。

This directive will not prevent focus from moving out of the trapped region due to mouse interaction.

可以使用 cdkFocusRegionStartcdkFocusRegionEndcdkFocusInitial 等 DOM 属性,来显式声明一个初始元素。cdkFocusInitial 用于指定在初始化该区域时哪个元素会获得焦点。cdkFocusRegionStartcdkFocusRegionEnd 定义了获得焦点陷阱的区域。使用 tab 键的时候,焦点会在这个区域内移动,并在区域的两端自动回卷。

Regions can be declared explicitly with an initial focus element by using the cdkFocusRegionStart, cdkFocusRegionEnd and cdkFocusInitial DOM attributes. cdkFocusInitial specifies the element that will receive focus upon initialization of the region. cdkFocusRegionStart and cdkFocusRegionEnd define the region within which focus will be trapped. When using the tab key, focus will move through this region and wrap around on either end.

例如:

For example:

<a mat-list-item routerLink cdkFocusRegionStart>Focus region start</a>
<a mat-list-item routerLink>Link</a>
<a mat-list-item routerLink cdkFocusInitial>Initially focused</a>
<a mat-list-item routerLink cdkFocusRegionEnd>Focus region end</a>

注意:如果你正在和 CdkTrapFocus 指令一起使用 cdkFocusInitial,那么除非你同时启用了 cdkTrapFocusAutoCapture 选项,否则什么都不会发生。这是因为 CdkTrapFocus 在初始化时默认不会捕获焦点。

Note: If you're using cdkFocusInitial together with the CdkTrapFocus directive, nothing will happen unless you've enabled the cdkTrapFocusAutoCapture option as well. This is due to CdkTrapFocus not capturing focus on initialization by default.

InteractivityChecker(交互检查器)

InteractivityChecker

InteractivityChecker 用于检查元素的交互性,它会捕获禁用(disabled)、可见(visible)、(可 tab)tabbable 和可获得焦点(focusable)状态,以便检查无障碍性。更多信息,请参阅 API 文档。

InteractivityChecker is used to check the interactivity of an element, capturing disabled, visible, tabbable, and focusable states for accessibility purposes. See the API docs for more details.

LiveAnnouncer(直播播音员)

LiveAnnouncer

LiveAnnouncer 用于通过 aria-live 区域为屏幕阅读器用户播报信息。关于 aria-live 区域的详细信息,请参见 W3C 的 WAI-ARIA

LiveAnnouncer is used to announce messages for screen-reader users using an aria-live region. See the W3C's WAI-ARIA for more information on aria-live regions.

@Component({...})
export class MyComponent {

 constructor(liveAnnouncer: LiveAnnouncer) {
   liveAnnouncer.announce("Hey Google");
 }
}

FocusMonitor(焦点管理器)

FocusMonitor

FocusMonitor 是一个可注入的服务,可以用来监听元素焦点状态的变化。它比单纯监听 focusblur 事件更有意义,因为它会告诉你该元素是如何获得焦点的(通过鼠标,键盘,触摸或编程方式)。如果需要,它还允许监听各级子元素。

The FocusMonitor is an injectable service that can be used to listen for changes in the focus state of an element. It's more powerful than just listening for focus or blur events because it tells you how the element was focused (via mouse, keyboard, touch, or programmatically). It also allows listening for focus on descendant elements if desired.

要监听某个元素的焦点变化,可以用 monitor 方法传入要监控的元素和一个可选的逻辑标志 checkChildren。给 checkChildren 传入 true 会告诉 FocusMonitor :如果该元素的任何各级子元素有焦点,就认为该元素有焦点。如果没有指定,该选项默认为 falsemonitor 方法会返回一个可观察对象,当焦点状态改变时,该对象会发送一个 FocusOriginFocusOrigin 是下列值之一:

To listen for focus changes on an element, use the monitor method which takes an element to monitor and an optional boolean flag checkChildren. Passing true for checkChildren will tell the FocusMonitor to consider the element focused if any of its descendants are focused. This option defaults to false if not specified. The monitor method will return an Observable that emits the FocusOrigin whenever the focus state changes. The FocusOrigin will be one of the following:

  • 'mouse' 表示该元素是通过鼠标获得焦点的

    'mouse' indicates the element was focused with the mouse

  • 'keyboard' 表示该元素是通过键盘获得焦点的

    'keyboard' indicates the element was focused with the keyboard

  • 'touch' 表示该元素是通过触摸屏获得焦点的

    'touch' indicates the element was focused by touching on a touchscreen

  • 'program' 表示该元素是通过编程方式获得焦点的

    'program' indicates the element was focused programmatically

  • null 表示该元素失去了焦点

    null indicates the element was blurred

除了在可观察对象中发送信息之外,FocusMonitor 还会自动对有焦点的元素元素应用一些 CSS 类。如果该元素拥有焦点,它会添加 .cdk-focused 类,并进一步添加 .cdk-${origin}-focused 类来表明元素是如何获得焦点的( ${origin}mousekeyboardtouchprogram 之一)。

In addition to emitting on the observable, the FocusMonitor will automatically apply CSS classes to the element when focused. It will add .cdk-focused if the element is focused and will further add .cdk-${origin}-focused (with ${origin} being mouse, keyboard, touch, or program) to indicate how the element was focused.

注意:目前,这个可观察对象会在 Angular Zone 之外发出 FocusMonitor。因此,如果你在其订阅中调用了 markForCheck,那就必须把自己放回 Angular Zone 内部。

Note: currently the FocusMonitor emits on the observable outside of the Angular zone. Therefore if you markForCheck in the subscription you must put yourself back in the Angular zone.

focusMonitor.monitor(el).subscribe(origin => this.ngZone.run(() => /* ... */ ));

任何通过调用 monitor 来监控的元素最终都要对这个元素调用 stopMonitoring 来取消监控。

Any element that is monitored by calling monitor should eventually be unmonitored by calling stopMonitoring with the same element.

Monitoring focus with FocusMonitor

Focus Monitored Subtree (blurred)

当使用 FocusMonitorfocusVia 方法来通过编程的方式设置焦点时,可能会伪造一个 FocusMonitor。使用该方法时要传入一个希望获得焦点的元素和 FocusOrigin。如果 FocusMonitor 当前正在监视要获得焦点的元素,它就会报告传入的这个 FocusOrigin。如果当前没有监视该元素,它就会像正常情况下一样获得焦点。

It is possible to falsify the FocusOrigin when setting the focus programmatically by using the focusVia method of FocusMonitor. This method accepts an element to focus and the FocusOrigin to use. If the element being focused is currently being monitored by the FocusMonitor it will report the FocusOrigin that was passed in. If the element is not currently being monitored it will just be focused like normal.

Focusing with a specific FocusOrigin

为了方便使用,CDK 还提供了两个指令,可以简单地监控某个元素。cdkMonitorElementFocus 相当于在 checkChildrenfalse 的宿主元素上调用 monitorcdkMonitorSubtreeFocus 相当于在 checkChildrentrue 的宿主元素上调用 monitor。这两个指令都有一个 @Output() cdkFocusChange,每当它发生变化时都会通过该事件发出新的 FocusOrigin

For convenience, the CDK also provides two directives that allow for easily monitoring an element. cdkMonitorElementFocus is the equivalent of calling monitor on the host element with checkChildren set to false. cdkMonitorSubtreeFocus is the equivalent of calling monitor on the host element with checkChildren set to true. Each of these directives has an @Output() cdkFocusChange that will emit the new FocusOrigin whenever it changes.

Monitoring focus with FocusMonitor

Focus Monitored Subtree (blurred)

样式实用工具

Styling utilities

CDK a11y 包中附带了一组 CSS 样式,可用于构建无障碍组件。要使用它们,你必须在全局样式表中包含这些样式。如果你正在和 CDK 一起使用 Material,那么就已经包含这些样式了。

The CDK a11y package comes with a set of CSS styles that can be used when building accessible components. To take advantage of them, you have to include the styles in your global stylesheet. If you're using Material together with the CDK, these styles have been included for you already.

@import '~@angular/cdk/text-field/text-field';

@include cdk-a11y();

屏幕阅读器和其它辅助技术在默认情况下都会跳过那些有 display: nonevisibility: hidden 等样式的元素。在某些情况下,你可能需要从视觉上隐藏一个元素,但同时让它可用于辅助技术。你可以用 cdk-visually-hidden 类来做到这一点:

By default, screen readers and other assistive technology will skip elements that have display: none, visibility: hidden etc. In some cases you may need to visually hide an element, while keeping it available for assistive technology. You can do so using the cdk-visually-hidden class:

<div class="custom-checkbox">
  <input type="checkbox" class="cdk-visually-hidden">
</div>

这个 a11y 包中提供了一个 mixin,它允许你找出那些打开了 Windows 高对比度模式的用户。为了支持这些高对比度用户,你可以用 cdk-high-contrast mixin 来包装你的样式。当 CDK 在运行期间发现处于高对比度环境时,就会在 body 上添加了某个 CSS 类,这个 mixin 就会以该 CSS 类为目标。

The a11y package offers a mixin that allows you to target users that have the Windows high contrast mode turned on. To target high contrast users, you can wrap your styles with the cdk-high-contrast mixin. The mixin works by targeting a CSS class which is added to the body by the CDK when high contrast mode is detected at runtime.

button {
  @include cdk-high-contrast {
    outline: solid 1px;
  }
}