Skip to content

Commit ff7a13b

Browse files
crisbetoandrewseguin
authored andcommitted
perf(focus-monitor): use passive touch listener (#7957)
The `FocusMonitor` works by binding a `touchstart` (as well as some other events) listener to the `document` in order to keep track of focus. These changes make the `touchstart` listener `passive`, which means that on supported browsers it won't block rendering and should make scrolling smoother.
1 parent 8168667 commit ff7a13b

File tree

4 files changed

+33
-5
lines changed

4 files changed

+33
-5
lines changed

src/cdk/a11y/focus-monitor.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe('FocusMonitor', () => {
8080

8181
it('should detect focus via touch', fakeAsync(() => {
8282
// Simulate focus via touch.
83-
dispatchMouseEvent(buttonElement, 'touchstart');
83+
dispatchFakeEvent(buttonElement, 'touchstart');
8484
buttonElement.focus();
8585
fixture.detectChanges();
8686
tick(TOUCH_BUFFER_MS);
@@ -262,7 +262,7 @@ describe('cdkMonitorFocus', () => {
262262

263263
it('should detect focus via touch', fakeAsync(() => {
264264
// Simulate focus via touch.
265-
dispatchMouseEvent(buttonElement, 'touchstart');
265+
dispatchFakeEvent(buttonElement, 'touchstart');
266266
buttonElement.focus();
267267
fixture.detectChanges();
268268
tick(TOUCH_BUFFER_MS);

src/cdk/a11y/focus-monitor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Platform} from '@angular/cdk/platform';
9+
import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform';
1010
import {
1111
Directive,
1212
ElementRef,
@@ -168,13 +168,16 @@ export class FocusMonitor {
168168
// When the touchstart event fires the focus event is not yet in the event queue. This means
169169
// we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to
170170
// see if a focus happens.
171-
document.addEventListener('touchstart', (event: Event) => {
171+
document.addEventListener('touchstart', (event: TouchEvent) => {
172172
if (this._touchTimeout != null) {
173173
clearTimeout(this._touchTimeout);
174174
}
175175
this._lastTouchTarget = event.target;
176176
this._touchTimeout = setTimeout(() => this._lastTouchTarget = null, TOUCH_BUFFER_MS);
177-
}, true);
177+
178+
// Note that we need to cast the event options to `any`, because at the time of writing
179+
// (TypeScript 2.5), the built-in types don't support the `addEventListener` options param.
180+
}, supportsPassiveEventListeners() ? ({passive: true, capture: true} as any) : true);
178181

179182
// Make a note of when the window regains focus, so we can restore the origin info for the
180183
// focused element.

src/cdk/platform/features.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
/** Cached result of whether the user's browser supports passive event listeners. */
10+
let supportsPassiveEvents: boolean;
11+
12+
/**
13+
* Checks whether the user's browser supports passive event listeners.
14+
* See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
15+
*/
16+
export function supportsPassiveEventListeners(): boolean {
17+
if (supportsPassiveEvents == null) {
18+
try {
19+
window.addEventListener('test', null!, Object.defineProperty({}, 'passive', {
20+
get: () => supportsPassiveEvents = true
21+
}));
22+
} finally {
23+
supportsPassiveEvents = supportsPassiveEvents || false;
24+
}
25+
}
26+
27+
return supportsPassiveEvents;
28+
}
29+
930
/** Cached result Set of input types support by the current browser. */
1031
let supportedInputTypes: Set<string>;
1132

src/demo-app/focus-origin/focus-origin-demo.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.demo-focusable {
2+
border: 2px solid transparent;
3+
}
4+
15
.demo-focusable.cdk-focused {
26
border: 2px solid red;
37
}

0 commit comments

Comments
 (0)