fix(a11y): aria-live directive announcing the same text multiple times (#13467)

The `CdkAriaLive` directive uses a `MutationObserver` to monitor for DOM content changes and to announce them through the `LiveAnnouncer`. Since the `MutationObserver` also fires for things like attribute changes, the same text can be announced more than once. This is visible on the calendar where the same text is announced twice when changing views.
This commit is contained in:
Kristiyan Kostadinov
2018-11-08 18:00:43 +00:00
committed by vivian-hu
parent 1ef16ac78a
commit a150494202
2 changed files with 25 additions and 2 deletions
@@ -231,6 +231,22 @@ describe('CdkAriaLive', () => {
expect(announcer.announce).toHaveBeenCalledWith('Newest content', 'assertive');
}));
it('should not announce the same text multiple times', fakeAsync(() => {
fixture.componentInstance.content = 'Content';
fixture.detectChanges();
invokeMutationCallbacks();
flush();
expect(announcer.announce).toHaveBeenCalledTimes(1);
fixture.detectChanges();
invokeMutationCallbacks();
flush();
expect(announcer.announce).toHaveBeenCalledTimes(1);
}));
});
@@ -131,14 +131,21 @@ export class CdkAriaLive implements OnDestroy {
.observe(this._elementRef)
.subscribe(() => {
// Note that we use textContent here, rather than innerText, in order to avoid a reflow.
const element = this._elementRef.nativeElement;
this._liveAnnouncer.announce(element.textContent, this._politeness);
const elementText = this._elementRef.nativeElement.textContent;
// The `MutationObserver` fires also for attribute
// changes which we don't want to announce.
if (elementText !== this._previousAnnouncedText) {
this._liveAnnouncer.announce(elementText, this._politeness);
this._previousAnnouncedText = elementText;
}
});
});
}
}
private _politeness: AriaLivePoliteness = 'off';
private _previousAnnouncedText?: string;
private _subscription: Subscription | null;
constructor(private _elementRef: ElementRef, private _liveAnnouncer: LiveAnnouncer,