@@ -14,6 +14,7 @@ import {
14
14
OverlayConfig ,
15
15
OverlayRef ,
16
16
ScrollStrategy ,
17
+ OverlayContainer ,
17
18
} from '@angular/cdk/overlay' ;
18
19
import { ComponentPortal , ComponentType , PortalInjector , TemplatePortal } from '@angular/cdk/portal' ;
19
20
import { Location } from '@angular/common' ;
@@ -67,6 +68,7 @@ export class MatDialog {
67
68
private _openDialogsAtThisLevel : MatDialogRef < any > [ ] = [ ] ;
68
69
private _afterAllClosedAtThisLevel = new Subject < void > ( ) ;
69
70
private _afterOpenAtThisLevel = new Subject < MatDialogRef < any > > ( ) ;
71
+ private _ariaHiddenElements = new Map < Element , string | null > ( ) ;
70
72
71
73
/** Keeps track of the currently-open dialogs. */
72
74
get openDialogs ( ) : MatDialogRef < any > [ ] {
@@ -96,7 +98,8 @@ export class MatDialog {
96
98
private _injector : Injector ,
97
99
@Optional ( ) location : Location ,
98
100
@Inject ( MAT_DIALOG_SCROLL_STRATEGY ) private _scrollStrategy ,
99
- @Optional ( ) @SkipSelf ( ) private _parentDialog : MatDialog ) {
101
+ @Optional ( ) @SkipSelf ( ) private _parentDialog : MatDialog ,
102
+ private _overlayContainer : OverlayContainer ) {
100
103
101
104
// Close all of the dialogs when the user goes forwards/backwards in history or when the
102
105
// location hash changes. Note that this usually doesn't include clicking on links (unless
@@ -127,6 +130,11 @@ export class MatDialog {
127
130
const dialogRef =
128
131
this . _attachDialogContent < T > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) ;
129
132
133
+ // If this is the first dialog that we're opening, hide all the non-overlay content.
134
+ if ( ! this . openDialogs . length ) {
135
+ this . _hideNonDialogContentFromAssistiveTechnology ( ) ;
136
+ }
137
+
130
138
this . openDialogs . push ( dialogRef ) ;
131
139
dialogRef . afterClosed ( ) . subscribe ( ( ) => this . _removeOpenDialog ( dialogRef ) ) ;
132
140
this . afterOpen . next ( dialogRef ) ;
@@ -298,12 +306,49 @@ export class MatDialog {
298
306
if ( index > - 1 ) {
299
307
this . openDialogs . splice ( index , 1 ) ;
300
308
301
- // no open dialogs are left, call next on afterAllClosed Subject
309
+ // If all the dialogs were closed, remove/restore the `aria-hidden`
310
+ // to a the siblings and emit to the `afterAllClosed` stream.
302
311
if ( ! this . openDialogs . length ) {
312
+ this . _ariaHiddenElements . forEach ( ( previousValue , element ) => {
313
+ if ( previousValue ) {
314
+ element . setAttribute ( 'aria-hidden' , previousValue ) ;
315
+ } else {
316
+ element . removeAttribute ( 'aria-hidden' ) ;
317
+ }
318
+ } ) ;
319
+
320
+ this . _ariaHiddenElements . clear ( ) ;
303
321
this . _afterAllClosed . next ( ) ;
304
322
}
305
323
}
306
324
}
325
+
326
+ /**
327
+ * Hides all of the content that isn't an overlay from assistive technology.
328
+ */
329
+ private _hideNonDialogContentFromAssistiveTechnology ( ) {
330
+ const overlayContainer = this . _overlayContainer . getContainerElement ( ) ;
331
+
332
+ // Ensure that the overlay container is attached to the DOM.
333
+ if ( overlayContainer . parentElement ) {
334
+ const siblings = overlayContainer . parentElement . children ;
335
+
336
+ for ( let i = siblings . length - 1 ; i > - 1 ; i -- ) {
337
+ let sibling = siblings [ i ] ;
338
+
339
+ if ( sibling !== overlayContainer &&
340
+ sibling . nodeName !== 'SCRIPT' &&
341
+ sibling . nodeName !== 'STYLE' &&
342
+ ! sibling . hasAttribute ( 'aria-live' ) ) {
343
+
344
+ this . _ariaHiddenElements . set ( sibling , sibling . getAttribute ( 'aria-hidden' ) ) ;
345
+ sibling . setAttribute ( 'aria-hidden' , 'true' ) ;
346
+ }
347
+ }
348
+ }
349
+
350
+ }
351
+
307
352
}
308
353
309
354
/**
0 commit comments