Skip to content

Commit

Permalink
perf(drag-drop): bind fewer touchmove listeners (#20404)
Browse files Browse the repository at this point in the history
On touch devices we have to bind the `touchmove` event before dragging has started, because iOS Safari won't allow us to call `preventDefault` on it otherwise. We also bind another `touchmove` listener once dragging starts which is what is used for moving the elements around on the screen.

These changes remove the dynamically-added `touchmove` listener and use the persistent one for everything instead.

(cherry picked from commit 2b1d84e)
  • Loading branch information
crisbeto authored and wagnermaciel committed Sep 16, 2020
1 parent 7a4d94b commit f7e0f31
Showing 1 changed file with 20 additions and 9 deletions.
29 changes: 20 additions & 9 deletions src/cdk/drag-drop/drag-drop-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class DragDropRegistry<I, C> implements OnDestroy {
this._ngZone.runOutsideAngular(() => {
// The event handler has to be explicitly active,
// because newer browsers make it passive by default.
this._document.addEventListener('touchmove', this._preventDefaultWhileDragging,
this._document.addEventListener('touchmove', this._persistentTouchmoveListener,
activeCapturingEventOptions);
});
}
Expand All @@ -100,7 +100,7 @@ export class DragDropRegistry<I, C> implements OnDestroy {
this.stopDragging(drag);

if (this._dragInstances.size === 0) {
this._document.removeEventListener('touchmove', this._preventDefaultWhileDragging,
this._document.removeEventListener('touchmove', this._persistentTouchmoveListener,
activeCapturingEventOptions);
}
}
Expand All @@ -120,18 +120,12 @@ export class DragDropRegistry<I, C> implements OnDestroy {

if (this._activeDragInstances.size === 1) {
const isTouchEvent = event.type.startsWith('touch');
const moveEvent = isTouchEvent ? 'touchmove' : 'mousemove';
const upEvent = isTouchEvent ? 'touchend' : 'mouseup';

// We explicitly bind __active__ listeners here, because newer browsers will default to
// passive ones for `mousemove` and `touchmove`. The events need to be active, because we
// use `preventDefault` to prevent the page from scrolling while the user is dragging.
this._globalListeners
.set(moveEvent, {
handler: (e: Event) => this.pointerMove.next(e as TouchEvent | MouseEvent),
options: activeCapturingEventOptions
})
.set(upEvent, {
.set(isTouchEvent ? 'touchend' : 'mouseup', {
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
options: true
})
Expand All @@ -150,6 +144,15 @@ export class DragDropRegistry<I, C> implements OnDestroy {
options: activeCapturingEventOptions
});

// We don't have to bind a move event for touch drag sequences, because
// we already have a persistent global one bound from `registerDragItem`.
if (!isTouchEvent) {
this._globalListeners.set('mousemove', {
handler: (e: Event) => this.pointerMove.next(e as MouseEvent),
options: activeCapturingEventOptions
});
}

this._ngZone.runOutsideAngular(() => {
this._globalListeners.forEach((config, name) => {
this._document.addEventListener(name, config.handler, config.options);
Expand Down Expand Up @@ -190,6 +193,14 @@ export class DragDropRegistry<I, C> implements OnDestroy {
}
}

/** Event listener for `touchmove` that is bound even if no dragging is happening. */
private _persistentTouchmoveListener = (event: TouchEvent) => {
if (this._activeDragInstances.size) {
event.preventDefault();
this.pointerMove.next(event);
}
}

/** Clears out the global event listeners from the `document`. */
private _clearGlobalListeners() {
this._globalListeners.forEach((config, name) => {
Expand Down

0 comments on commit f7e0f31

Please sign in to comment.