I have a focus trap that activates when an angular material dialog is opened. A parent component contains the cdkTrapFocus directive from the a11yModule (https://material.angular.io/cdk/a11y/api#CdkTrapFocus), and it's various children can have a varying number of inputs.
The focus trap works as expected when the dialog is opened, preventing the user from pressing tab or shift-tab to access items behind the dialog. However, when a user clicks on the grayed-out area behind the dialog, then presses shift-tab, they gain access to all items behind the dialog.
The focus trap remains broken until they press shift-tab (or tab) enough times to move the focus back inside the dialog. The correct behavior would be that the focus only ever stays trapped in the dialog, no matter what is clicked on inside or outside the dialog.
For the most part I have examined the link above to see what options there are to ensure focus remains trapped, but It has been difficult to determine what directives would be most useful for this problem. Below is the parent component html template.
<div class="dialog-frame dialog-fade dialog-backgray" [ngClass]="{ 'in': shown }" role="dialog">
<div class="dialog-main" [style.width]="dialogWidth" id="dialog-main" cdkTrapFocus>
<ng-template #element> </ng-template>
</div>
</div>
If anyone reading this has a solution to this, I would appreciate your help.
I ran into the shift-tab issue. The solution for me was to add a keydown listener, which prevents focus if the event path is not in the modal:
@HostListener('document:keydown', ['$event'])
onTab(event) {
if (event.key == 'Tab' && this.modalIsOpen) {
let path = event.composedPath();
let modal = path.find((element) => element.tagName && element.tagName.toLowerCase() == this.modalComponentTagName);
if (path && !modal) {
// Tabbing outside of the modal.
event.preventDefault();
this.focusOnFirstElementInModal();
}
}
}
For me this.modalIsOpen
is this.document.body.classList.contains('modal-open')
, this.modalComponentTagName
is 'app-donate-modal'
, and this.focusOnFirstElementInModal()
focuses on the modal's close button.