Skip to content

Commit

Permalink
fix(focus-trap): not attaching correctly if element is not in the DOM…
Browse files Browse the repository at this point in the history
… on init (#7665)
  • Loading branch information
crisbeto authored and josephperrott committed Jun 29, 2018
1 parent 1e164b6 commit 80d9a9a
Showing 1 changed file with 37 additions and 18 deletions.
55 changes: 37 additions & 18 deletions src/cdk/a11y/focus-trap/focus-trap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
Input,
NgZone,
OnDestroy,
DoCheck,
} from '@angular/core';
import {take} from 'rxjs/operators';
import {InteractivityChecker} from '../interactivity-checker/interactivity-checker';
Expand All @@ -32,6 +33,7 @@ import {InteractivityChecker} from '../interactivity-checker/interactivity-check
export class FocusTrap {
private _startAnchor: HTMLElement | null;
private _endAnchor: HTMLElement | null;
private _hasAttached = false;

/** Whether the focus trap is active. */
get enabled(): boolean { return this._enabled; }
Expand Down Expand Up @@ -72,30 +74,34 @@ export class FocusTrap {
/**
* Inserts the anchors into the DOM. This is usually done automatically
* in the constructor, but can be deferred for cases like directives with `*ngIf`.
* @returns Whether the focus trap managed to attach successfuly. This may not be the case
* if the target element isn't currently in the DOM.
*/
attachAnchors(): void {
if (!this._startAnchor) {
this._startAnchor = this._createAnchor();
}

if (!this._endAnchor) {
this._endAnchor = this._createAnchor();
attachAnchors(): boolean {
// If we're not on the browser, there can be no focus to trap.
if (this._hasAttached) {
return true;
}

this._ngZone.runOutsideAngular(() => {
this._startAnchor!.addEventListener('focus', () => {
this.focusLastTabbableElement();
});

this._endAnchor!.addEventListener('focus', () => {
this.focusFirstTabbableElement();
});
if (!this._startAnchor) {
this._startAnchor = this._createAnchor();
this._startAnchor!.addEventListener('focus', () => this.focusLastTabbableElement());
}

if (this._element.parentNode) {
this._element.parentNode.insertBefore(this._startAnchor!, this._element);
this._element.parentNode.insertBefore(this._endAnchor!, this._element.nextSibling);
if (!this._endAnchor) {
this._endAnchor = this._createAnchor();
this._endAnchor!.addEventListener('focus', () => this.focusFirstTabbableElement());
}
});

if (this._element.parentNode) {
this._element.parentNode.insertBefore(this._startAnchor!, this._element);
this._element.parentNode.insertBefore(this._endAnchor!, this._element.nextSibling);
this._hasAttached = true;
}

return this._hasAttached;
}

/**
Expand Down Expand Up @@ -217,6 +223,13 @@ export class FocusTrap {
return !!redirectToElement;
}

/**
* Checks whether the focus trap has successfuly been attached.
*/
hasAttached(): boolean {
return this._hasAttached;
}

/** Get the first tabbable element from a DOM subtree (inclusive). */
private _getFirstTabbableElement(root: HTMLElement): HTMLElement | null {
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
Expand Down Expand Up @@ -313,7 +326,7 @@ export class FocusTrapFactory {
selector: '[cdkTrapFocus]',
exportAs: 'cdkTrapFocus',
})
export class CdkTrapFocus implements OnDestroy, AfterContentInit {
export class CdkTrapFocus implements OnDestroy, AfterContentInit, DoCheck {
private _document: Document;

/** Underlying FocusTrap instance. */
Expand Down Expand Up @@ -364,4 +377,10 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit {
this.focusTrap.focusInitialElementWhenReady();
}
}

ngDoCheck() {
if (!this.focusTrap.hasAttached()) {
this.focusTrap.attachAnchors();
}
}
}

0 comments on commit 80d9a9a

Please sign in to comment.