diff --git a/src/cdk/a11y/focus-monitor.spec.ts b/src/cdk/a11y/focus-monitor.spec.ts index 1b53d56be5d3..3602cd2aedfd 100644 --- a/src/cdk/a11y/focus-monitor.spec.ts +++ b/src/cdk/a11y/focus-monitor.spec.ts @@ -186,14 +186,28 @@ describe('FocusMonitor', () => { fixture.detectChanges(); tick(); - expect(buttonElement.classList.length) - .toBe(2, 'button should have exactly 2 focus classes'); + expect(buttonElement.classList.length).toBe(2, 'button should have exactly 2 focus classes'); focusMonitor.stopMonitoring(buttonElement); fixture.detectChanges(); expect(buttonElement.classList.length).toBe(0, 'button should not have any focus classes'); })); + + it('should remove classes when destroyed', fakeAsync(() => { + buttonElement.focus(); + fixture.detectChanges(); + tick(); + + expect(buttonElement.classList.length).toBe(2, 'button should have exactly 2 focus classes'); + + // Destroy manually since destroying the fixture won't do it. + focusMonitor.ngOnDestroy(); + fixture.detectChanges(); + + expect(buttonElement.classList.length).toBe(0, 'button should not have any focus classes'); + })); + }); diff --git a/src/cdk/a11y/focus-monitor.ts b/src/cdk/a11y/focus-monitor.ts index ae8cc1f8eb3c..4b3158c3cedc 100644 --- a/src/cdk/a11y/focus-monitor.ts +++ b/src/cdk/a11y/focus-monitor.ts @@ -42,7 +42,7 @@ type MonitoredElementInfo = { /** Monitors mouse and keyboard events to determine the cause of focus events. */ @Injectable() -export class FocusMonitor { +export class FocusMonitor implements OnDestroy { /** The focus origin that the next focus event is a result of. */ private _origin: FocusOrigin = null; @@ -58,8 +58,8 @@ export class FocusMonitor { /** The timeout id of the touch timeout, used to cancel timeout later. */ private _touchTimeout: number; - /** Weak map of elements being monitored to their info. */ - private _elementInfo = new WeakMap(); + /** Map of elements being monitored to their info. */ + private _elementInfo = new Map(); /** A map of global objects to lists of current listeners. */ private _unregisterGlobalListeners = () => {}; @@ -135,7 +135,7 @@ export class FocusMonitor { * @param element The element to stop monitoring. */ stopMonitoring(element: HTMLElement): void { - let elementInfo = this._elementInfo.get(element); + const elementInfo = this._elementInfo.get(element); if (elementInfo) { elementInfo.unlisten(); @@ -157,6 +157,10 @@ export class FocusMonitor { element.focus(); } + ngOnDestroy() { + this._elementInfo.forEach((_info, element) => this.stopMonitoring(element)); + } + /** Register necessary event listeners on the document and window. */ private _registerGlobalListeners() { // Do nothing if we're not on the browser platform.