fix(a11y): not being able to escape disabled focus trap using arrow keys (#13133)

Currently when a focus trap is disabled, we set the `tabindex` of the anchors to -1 in order to allow people to tab out of it. This doesn't work if somebody is navigating with the arrow keys using a screen reader, because the element is still focusable which means that the screen reader will focus it eventually, causing focus to be trapped. These changes remove the `tabindex` if the focus trap is disabled instead.

**Note:** An alternate approach to this can be to hide the element using `display: none`, but I opted to remove the `tabindex` in order to avoid a style recalculation.

Fixes #13132.
This commit is contained in:
Kristiyan Kostadinov
2018-10-02 22:31:30 +02:00
committed by vivian-hu
parent 730e6a30a1
commit 3c55caaf1e
2 changed files with 17 additions and 5 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ describe('FocusTrap', () => {
fixture.componentInstance._isFocusTrapEnabled = false;
fixture.detectChanges();
expect(anchors.every(current => current.getAttribute('tabindex') === '-1')).toBe(true);
expect(anchors.every(current => !current.hasAttribute('tabindex'))).toBe(true);
});
});
+16 -4
View File
@@ -37,11 +37,12 @@ export class FocusTrap {
/** Whether the focus trap is active. */
get enabled(): boolean { return this._enabled; }
set enabled(val: boolean) {
this._enabled = val;
set enabled(value: boolean) {
this._enabled = value;
if (this._startAnchor && this._endAnchor) {
this._startAnchor.tabIndex = this._endAnchor.tabIndex = this._enabled ? 0 : -1;
this._toggleAnchorTabIndex(value, this._startAnchor);
this._toggleAnchorTabIndex(value, this._endAnchor);
}
}
private _enabled: boolean = true;
@@ -278,12 +279,23 @@ export class FocusTrap {
/** Creates an anchor element. */
private _createAnchor(): HTMLElement {
const anchor = this._document.createElement('div');
anchor.tabIndex = this._enabled ? 0 : -1;
this._toggleAnchorTabIndex(this._enabled, anchor);
anchor.classList.add('cdk-visually-hidden');
anchor.classList.add('cdk-focus-trap-anchor');
return anchor;
}
/**
* Toggles the `tabindex` of an anchor, based on the enabled state of the focus trap.
* @param isEnabled Whether the focus trap is enabled.
* @param anchor Anchor on which to toggle the tabindex.
*/
private _toggleAnchorTabIndex(isEnabled: boolean, anchor: HTMLElement) {
// Remove the tabindex completely, rather than setting it to -1, because if the
// element has a tabindex, the user might still hit it when navigating with the arrow keys.
isEnabled ? anchor.setAttribute('tabindex', '0') : anchor.removeAttribute('tabindex');
}
/** Executes a function when the zone is stable. */
private _executeOnStable(fn: () => any): void {
if (this._ngZone.isStable) {