Skip to content

Commit 8a3d21a

Browse files
crisbetojelbourn
authored andcommitted
fix(drag-drop): showing touch device tap highlight when using a handle (#14549)
Currently we disable all native drag interactions on the elements that we bind events to. Since when a `cdkDrag` has a handle, we don't remove its tap highlight which means that iOS will show a dark overlay over it. These changes disable the tap highlight while dragging.
1 parent c5bdab8 commit 8a3d21a

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,58 @@ describe('CdkDrag', () => {
684684
.toBe('translate3d(50px, 100px, 0px)', 'Expected to drag the element by its handle.');
685685
}));
686686

687+
it('should disable the tap highlight while dragging via the handle', fakeAsync(() => {
688+
// This test is irrelevant if the browser doesn't support styling the tap highlight color.
689+
if (!('webkitTapHighlightColor' in document.body.style)) {
690+
return;
691+
}
692+
693+
const fixture = createComponent(StandaloneDraggableWithHandle);
694+
fixture.detectChanges();
695+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
696+
const handle = fixture.componentInstance.handleElement.nativeElement;
697+
698+
expect(dragElement.style.webkitTapHighlightColor).toBeFalsy();
699+
700+
startDraggingViaMouse(fixture, handle);
701+
702+
expect(dragElement.style.webkitTapHighlightColor).toBe('transparent');
703+
704+
dispatchMouseEvent(document, 'mousemove', 50, 100);
705+
fixture.detectChanges();
706+
707+
dispatchMouseEvent(document, 'mouseup', 50, 100);
708+
fixture.detectChanges();
709+
710+
expect(dragElement.style.webkitTapHighlightColor).toBeFalsy();
711+
}));
712+
713+
it('should preserve any existing `webkitTapHighlightColor`', fakeAsync(() => {
714+
// This test is irrelevant if the browser doesn't support styling the tap highlight color.
715+
if (!('webkitTapHighlightColor' in document.body.style)) {
716+
return;
717+
}
718+
719+
const fixture = createComponent(StandaloneDraggableWithHandle);
720+
fixture.detectChanges();
721+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
722+
const handle = fixture.componentInstance.handleElement.nativeElement;
723+
724+
dragElement.style.webkitTapHighlightColor = 'purple';
725+
726+
startDraggingViaMouse(fixture, handle);
727+
728+
expect(dragElement.style.webkitTapHighlightColor).toBe('transparent');
729+
730+
dispatchMouseEvent(document, 'mousemove', 50, 100);
731+
fixture.detectChanges();
732+
733+
dispatchMouseEvent(document, 'mouseup', 50, 100);
734+
fixture.detectChanges();
735+
736+
expect(dragElement.style.webkitTapHighlightColor).toBe('purple');
737+
}));
738+
687739
});
688740

689741
describe('in a drop container', () => {

src/cdk/drag-drop/drag-ref.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ export class DragRef<T = any> {
155155
*/
156156
private _rootElement: HTMLElement;
157157

158+
/**
159+
* Inline style value of `-webkit-tap-highlight-color` at the time the
160+
* dragging was started. Used to restore the value once we're done dragging.
161+
*/
162+
private _rootElementTapHighlight: string | null;
163+
158164
/** Subscription to pointer movement events. */
159165
private _pointerMoveSubscription = Subscription.EMPTY;
160166

@@ -502,6 +508,10 @@ export class DragRef<T = any> {
502508
this._removeSubscriptions();
503509
this._dragDropRegistry.stopDragging(this);
504510

511+
if (this._handles) {
512+
this._rootElement.style.webkitTapHighlightColor = this._rootElementTapHighlight;
513+
}
514+
505515
if (!this._hasStartedDragging) {
506516
return;
507517
}
@@ -567,6 +577,7 @@ export class DragRef<T = any> {
567577
const isDragging = this.isDragging();
568578
const isTouchSequence = isTouchEvent(event);
569579
const isAuxiliaryMouseButton = !isTouchSequence && (event as MouseEvent).button !== 0;
580+
const rootElement = this._rootElement;
570581
const isSyntheticEvent = !isTouchSequence && this._lastTouchEventTime &&
571582
this._lastTouchEventTime + MOUSE_EVENT_IGNORE_TIME > Date.now();
572583

@@ -591,6 +602,14 @@ export class DragRef<T = any> {
591602
this._initialTransform = this._rootElement.style.transform || '';
592603
}
593604

605+
// If we've got handles, we need to disable the tap highlight on the entire root element,
606+
// otherwise iOS will still add it, even though all the drag interactions on the handle
607+
// are disabled.
608+
if (this._handles.length) {
609+
this._rootElementTapHighlight = rootElement.style.webkitTapHighlightColor;
610+
rootElement.style.webkitTapHighlightColor = 'transparent';
611+
}
612+
594613
this._toggleNativeDragInteractions();
595614
this._hasStartedDragging = this._hasMoved = false;
596615
this._initialContainer = this.dropContainer!;

0 commit comments

Comments
 (0)