无障碍性 Accessibility

a11y 概览

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

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

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.

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 用于检查元素的交互性,它会捕获禁用(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 用于通过 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 是一个可注入的服务,可以用来监听元素焦点状态的变化。它比单纯监听 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 the 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)

cdk/a11y 包中附带了一组 Sass Mixin,可生成一些有用的样式,以构建无障碍化体验。

The cdk/a11y package comes with Sass mixins that produce styles useful for building accessible experiences.

屏幕阅读器和其它辅助科技会跳过带有 display: nonevisibility: hiddenopacity: 0height: 0width: 0 的元素。某些情况下,你需要在视觉上隐藏掉某个元素,但是仍然让它对辅助科技可用。你可以使用 Sass Mixin a11y-visually-hidden 来实现这一点,它会生成一个 CSS 类 .cdk-visually-hidden

Screen readers and other assistive technology skip elements that have display: none, visibility: hidden, opacity: 0, height: 0, or width: 0. In some cases you may need to visually hide an element while keeping it available to assistive technology. You can do so using the a11y-visually-hidden Sass mixin, which emits the .cdk-visually-hidden CSS class.

如果你正在使用 Angular Material,这个类会自动被 Angular Material 的主题体系包含尽量。否则,你就要在全局样式表中包含这个 Mixin。

If you're using Angular Material, this class is included automatically by Angular Material's theming system. Otherwise, you can include this mixin in a global stylesheet.

@use '~@angular/cdk';

@include cdk.a11y-visually-hidden();
<div class="custom-checkbox">
  <input type="checkbox" class="cdk-visually-hidden">
</div>

Microsoft Windows 包含一种称为Windows 高对比度模式的无障碍功能。cdk/a11y 包提供了一个 Sass mixin,可让你定义仅适用于高对比度模式的样式。要创建高对比度样式,请在 high-contrast mixin 中定义样式。

Microsoft Windows includes an accessibility feature called [Windows High Contrast Mode][]. The cdk/a11y package provides a Sass mixin that lets you define styles that only apply in high contrast mode. To create a high contrast style, define your style inside the high-contrast mixin.

这个 Mixin 会借助某个 CSS 类来起作用。在运行期间,当 CDK 借助 HighContrastModeDetector 服务检测到高对比度模式时,就会把这个类添加到 body 上。

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, via the HighContrastModeDetector service.

@use '~@angular/cdk';

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

high-contrast 这个 mixin 接受两个可选参数 $target$encapsulation

The high-contrast mixin accepts two optional parameters, $target and $encapsulation.

$target 参数允许你指定这些样式要用高对比度模式的哪个变体。接受的值是 active (默认),black-on-whitewhite-on-black 。这些值对应于 -ms-high-contrast 媒体查询 所支持的值。

The $target parameter allows you to specify which variation of high contrast mode your style targets. The accepted values are active (default), black-on-white, and white-on-black. These values correspond to the supported values for the -ms-high-contrast media query.

$encapsulation 参数会影响所生成的样式与样式封装的互动。支持的值是 onoffany 。默认值为 any,它生成的两个选择器,可用于任何封装方案。指定为 onoff,可以将样式分别限制为启用或禁用样式封装,可以轻微减小生成的 CSS 的大小。这些为带封装的组件生成的样式,会作用于 Angular 的模拟(emulated)样式封装和原生(native) Shadow DOM 封装方式。

The $encapsulation parameter affects how the emitted styles interact with style encapsulation. The supported values are on, off, and any. The default value is any, which works for any encapsulation scenario by emitting two selectors. Specifying either on or off slightly reduces the amount of CSS emitted by limiting the styles to components with encapsulation enabled or disabled, respectively. The styles emitted for encapsulated components work for both Angular's emulated style encapsulation and for native Shadow DOM encapsulation.