diff --git a/src/cdk/a11y/focus-monitor.spec.ts b/src/cdk/a11y/focus-monitor.spec.ts index 41370ab208ce..693ca9e958c6 100644 --- a/src/cdk/a11y/focus-monitor.spec.ts +++ b/src/cdk/a11y/focus-monitor.spec.ts @@ -80,7 +80,7 @@ describe('FocusMonitor', () => { it('should detect focus via touch', fakeAsync(() => { // Simulate focus via touch. - dispatchMouseEvent(buttonElement, 'touchstart'); + dispatchFakeEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); tick(TOUCH_BUFFER_MS); @@ -262,7 +262,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via touch', fakeAsync(() => { // Simulate focus via touch. - dispatchMouseEvent(buttonElement, 'touchstart'); + dispatchFakeEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); tick(TOUCH_BUFFER_MS); diff --git a/src/cdk/a11y/focus-monitor.ts b/src/cdk/a11y/focus-monitor.ts index 8d63982b67cc..a1d34a7ff988 100644 --- a/src/cdk/a11y/focus-monitor.ts +++ b/src/cdk/a11y/focus-monitor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Platform} from '@angular/cdk/platform'; +import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform'; import { Directive, ElementRef, @@ -168,13 +168,16 @@ export class FocusMonitor { // When the touchstart event fires the focus event is not yet in the event queue. This means // we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to // see if a focus happens. - document.addEventListener('touchstart', (event: Event) => { + document.addEventListener('touchstart', (event: TouchEvent) => { if (this._touchTimeout != null) { clearTimeout(this._touchTimeout); } this._lastTouchTarget = event.target; this._touchTimeout = setTimeout(() => this._lastTouchTarget = null, TOUCH_BUFFER_MS); - }, true); + + // Note that we need to cast the event options to `any`, because at the time of writing + // (TypeScript 2.5), the built-in types don't support the `addEventListener` options param. + }, supportsPassiveEventListeners() ? ({passive: true, capture: true} as any) : true); // Make a note of when the window regains focus, so we can restore the origin info for the // focused element. diff --git a/src/cdk/platform/features.ts b/src/cdk/platform/features.ts index b6b82c3636b4..3ea2dad061d7 100644 --- a/src/cdk/platform/features.ts +++ b/src/cdk/platform/features.ts @@ -6,6 +6,27 @@ * found in the LICENSE file at https://angular.io/license */ +/** Cached result of whether the user's browser supports passive event listeners. */ +let supportsPassiveEvents: boolean; + +/** + * Checks whether the user's browser supports passive event listeners. + * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + */ +export function supportsPassiveEventListeners(): boolean { + if (supportsPassiveEvents == null) { + try { + window.addEventListener('test', null!, Object.defineProperty({}, 'passive', { + get: () => supportsPassiveEvents = true + })); + } finally { + supportsPassiveEvents = supportsPassiveEvents || false; + } + } + + return supportsPassiveEvents; +} + /** Cached result Set of input types support by the current browser. */ let supportedInputTypes: Set; diff --git a/src/demo-app/focus-origin/focus-origin-demo.scss b/src/demo-app/focus-origin/focus-origin-demo.scss index ddfd64de6bb3..f972dda6e39a 100644 --- a/src/demo-app/focus-origin/focus-origin-demo.scss +++ b/src/demo-app/focus-origin/focus-origin-demo.scss @@ -1,3 +1,7 @@ +.demo-focusable { + border: 2px solid transparent; +} + .demo-focusable.cdk-focused { border: 2px solid red; }