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.
This commit is contained in:
Kristiyan Kostadinov
2017-11-02 04:31:40 +01:00
committed by Andrew Seguin
parent 816866738a
commit ff7a13b053
4 changed files with 33 additions and 5 deletions
+2 -2
View File
@@ -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);
+6 -3
View File
@@ -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.
+21
View File
@@ -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<string>;
@@ -1,3 +1,7 @@
.demo-focusable {
border: 2px solid transparent;
}
.demo-focusable.cdk-focused {
border: 2px solid red;
}