timruffles / mobile-drag-drop

A drop-in shim to allow you to use existing html5 drag'n'drop code with mobile browsers
http://timruffles.github.io/mobile-drag-drop/demo/
MIT License
603 stars 151 forks source link

DragEnd event not firing consistently on Android Chrome - Pixel Oreo? #114

Open IanOvenden opened 6 years ago

IanOvenden commented 6 years ago

Hi,

I've recently implemented this shim into a React project of mine to provide drag and drop support for mobile devices. I was able to get this up and running pretty quickly and it worked great in simulation mode on desktop. However I was noticing some issues when testing on a physical device.

I hooked up the device to Chrome dev tools for some remote debugging and noted that the dragend event was not always being fired upon release. When you touch the screen again, it seems that the item you've just dropped reattaches to your finger position - when dropped a second time, the event fires. It doesn't seem to make any difference if you drop the element on a valid drop zone (fires drop event) or not (cancelled).

To eliminate the React layer from the problem, I stripped the code right back and created a pretty straightforward Codepen with just my drag and drop code. This contains a scroll-able div so you can drag an item, scrolling the div before dropping in one of two available drop zones.

https://codepen.io/ianovenden/pen/WZEgmg

The issue still exists. If you open the pen in full window mode full window it works fine on desktop and in simulation mode - I've added a rudimentary console to output the "DRAG END" text if the event fires (this clears after 2 seconds). However, if you open the same pen full screen in Chrome the dragend event fires intermittently. It works fine in mobile Firefox.

Issue on:

Device: Google Pixel Chrome version: 61.0.3163.98 Android version: 8.0.0

Works as expected on:

Device: Google Pixel Firefox version: 56.0 Android version: 8.0.0

Device: Nexus 7 Chrome version: 61.0.3163.98 Android version: 6.0.1

Appreciate your work :+1:

reppners commented 6 years ago

Thanks for the detailed report. I'm currently in babymoon and will be back in 2-3 weeks.

I don't have access to a Google Pixel but I will try to support you as much as I can to solve this issue.

To debug this further please include the unminified polyfill (index.js) and post the console output that is generated when performing the failing drag and drop.

IanOvenden commented 6 years ago

Conrats! :)

Please find below the console output when the dropend event fails to fire:

index.ts:156 dnd-poly: global touchstart
index.ts:337 dnd-poly: setting up potential drag operation..
index.ts:404 dnd-poly: starting drag and drop operation

Post failure, the next time I touch the screen it seems to complete the operation...

index.ts:404 dnd-poly: starting drag and drop operation
index.ts:1480 dnd-poly: dispatching dragstart
5index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragenter
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
3index.ts:583 dnd-poly: moving draggable..
index.ts:156 dnd-poly: global touchstart
index.ts:163 dnd-poly: drag operation already active
4index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:925 dnd-poly: drag operation end detected with none
index.ts:1480 dnd-poly: dispatching dragleave
index.ts:1388 dnd-poly: no transition used - skipping snapback
index.ts:1053 dnd-poly: dragimage snap back transition ended
index.ts:1480 dnd-poly: dispatching dragend
pen.js:40 DRAG END
index.ts:511 dnd-poly: cleanup
reppners commented 6 years ago

Just to clarify, does the line

index.ts:404 dnd-poly: starting drag and drop operation

occur twice in a row or just once?

IanOvenden commented 6 years ago

I'm only seeing it occur once.

reppners commented 6 years ago

When the dragend does fail with the initial touch interaction is the drag image displayed and you can move it around? Do any of the other events related to the drag operation fire? Based on the logs they don't and even the drag image is not moved around.

So the initial touch causes just these logs

index.ts:156 dnd-poly: global touchstart
index.ts:337 dnd-poly: setting up potential drag operation..

and only the second touch interaction will start the drag and drop

index.ts:404 dnd-poly: starting drag and drop operation
index.ts:1480 dnd-poly: dispatching dragstart
5index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragenter
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
3index.ts:583 dnd-poly: moving draggable..
index.ts:156 dnd-poly: global touchstart
index.ts:163 dnd-poly: drag operation already active
4index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:724 dnd-poly: new immediate user selection is: [object HTMLHtmlElement]
index.ts:1480 dnd-poly: dispatching dragover
index.ts:866 dnd-poly: dragover not prevented on possible drop-target.
index.ts:894 dnd-poly: d'n'd iteration ended. current drag operation: none
index.ts:583 dnd-poly: moving draggable..
index.ts:1480 dnd-poly: dispatching drag
index.ts:925 dnd-poly: drag operation end detected with none
index.ts:1480 dnd-poly: dispatching dragleave
index.ts:1388 dnd-poly: no transition used - skipping snapback
index.ts:1053 dnd-poly: dragimage snap back transition ended
index.ts:1480 dnd-poly: dispatching dragend
pen.js:40 DRAG END
index.ts:511 dnd-poly: cleanup

Is that correct?

IanOvenden commented 6 years ago

The drag image is displayed and can be moved around. It can even be dropped in an active drop zone. However, none of the events are firing/being logged.

Here's a further example of how I'm recreating the issue using my CodePen example - I'm using my Google Pixel (Android version: 8.0.0, Chrome version is now 62.0.3202.84) connected to Google Canary Dev Tools. All interactions are being made on the device itself.

  1. On initial load the polyfill is applied

1

  1. Here, I have picked up an item and dragged it to an active drop zone. As you can see, it has moved but no events have been fired according to the log other than the initial "setting up potential drag operation" :

2

  1. In image 3, I've actually picked up the top "Drag Me" element on the left, but I'm now moving around the element previously dropped on the right. It's as though the one on the right is still the actively dragged element even though it was already moved and dropped.

3

  1. The image below illustrates the log output when I touch the screen for a second time - it appears to complete the operation. I've highlighted the first line of the new log output.

4

This issue is intermittent, but does occur fairly frequently. One thing I have noticed, which may or may not be relevant - it seems to occur more when I deliberately hold the drag element before dragging (I have holdToDrag set at 500).

reppners commented 6 years ago

Thats detailed! Thanks 👍

Disabling holdToDrag does not change anything I presume?

IanOvenden commented 6 years ago

Apologies, I didn't think this made much difference but I've just double checked it and disabling the "holdToDrag" does make it work. Obviously in order to work smoothly on mobile the holdToDrag is pretty essential. My example was built with the view that you can swipe and scroll the viewport without accidentally dragging any elements.

reppners commented 6 years ago

Cool, this will make it easier to track it down and improve on the "holdToDrag" code.

An alternative to holdToDrag is a dedicated drag handle which enables the drag but the rest of the element allows scrolling.

You can look up the demo for an example of drag handle implementation: https://github.com/timruffles/ios-html5-drag-drop-shim/blob/master/demo/index.html#L515-L530

But nonetheless holdToDrag needs to improve so it can be used without issues.

reppners commented 6 years ago

I enhanced logging and applied a small fix regarding event listeners used for holdToDrag.

You can grab the files from here to test: https://github.com/timruffles/ios-html5-drag-drop-shim/tree/hold-to-drag-issue/release

IanOvenden commented 6 years ago

Hi,

Quick update.. I've switched my codepen to point at the branch assets (this appears to be correct):

<script src="https://rawgit.com/timruffles/ios-html5-drag-drop-shim/hold-to-drag-issue/release/index.min.js"></script>
  <!--optional import of scroll behaviour-->
  <script src="https://rawgit.com/timruffles/ios-html5-drag-drop-shim/hold-to-drag-issue/release/scroll-behaviour.min.js"></script>
  <script>
    // options are optional ;)
    MobileDragDrop.polyfill({
      // use this to make use of the scroll behaviour
      // dragStartConditionOverride: false,
      // dragImageCenterOnTouch: true,
      // iterationInterval: 1000,
      forceApply: true,
      holdToDrag: 500,
      dragImageTranslateOverride: MobileDragDrop.scrollBehaviourDragImageTranslateOverride
    });
  </script>

But the following exception is now thrown...

MobileDragDrop.polyfill is not a function

Did I miss something in swapping to this branch?

reppners commented 6 years ago

Oh.. I updated tooling and now rollup causes #118

Can you remove the scroll-behavior script tag while testing on this new branch until I get #118 fixed?

IanOvenden commented 6 years ago

Sure :+1:

Some quick, initial testing sees the same issue occurring - still intermittently

  1. polyfill is applied on load.
dnd-poly: Applying mobile drag and drop polyfill.
index.ts:137 dnd-poly: holdToDrag set to 500
  1. Initial move of one element. It clearly moves and is dropped in the drop zone, but I'd expect to see more logging in the console?

2

  1. The same occurs when moving a second element to the drop zone - only a few lines are logged to the console:

3

  1. I tried to move the second element again, it doesn't move correctly:

4

  1. When I touch the screen again, the following log output is generated and the previous drag and drop operation looks to complete.

Console listed below:

dnd-poly: setup delayed dragstart..
index.ts:1552 dnd-poly: starting delayed drag..
index.ts:161 dnd-poly: touchstart
index.ts:168 dnd-poly: drag operation already active
index.ts:410 dnd-poly: starting drag and drop operation
index.ts:1486 dnd-poly: dispatching dragstart
8index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragenter
index.ts:782 dnd-poly: dragenter default prevented
index.ts:1486 dnd-poly: dispatching dragover
index.ts:894 dnd-poly: dragover prevented.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: move
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:894 dnd-poly: dragover prevented.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: move
6index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragexit
index.ts:1486 dnd-poly: dispatching dragenter
index.ts:852 dnd-poly: current drop target changed.
index.ts:1486 dnd-poly: dispatching dragleave
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
6index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
8index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
6index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
8index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:730 dnd-poly: new immediate user selection is: [object HTMLDivElement]
index.ts:1486 dnd-poly: dispatching dragover
index.ts:872 dnd-poly: dragover not prevented on possible drop-target.
index.ts:900 dnd-poly: d'n'd iteration ended. current drag operation: none
7index.ts:589 dnd-poly: moving draggable..
index.ts:1486 dnd-poly: dispatching drag
index.ts:931 dnd-poly: drag operation end detected with none
index.ts:1486 dnd-poly: dispatching dragleave
index.ts:1394 dnd-poly: no transition used - skipping snapback
index.ts:1059 dnd-poly: dragimage snap back transition ended
index.ts:1486 dnd-poly: dispatching dragend
pen.js:40 DRAG END
index.ts:517 dnd-poly: cleanup

Thanks.

reppners commented 6 years ago

Frankly.. I'm a bit lost - can you activate the timestamps in the chrome console and repeat the procedure? https://stackoverflow.com/questions/12008120/console-log-timestamps-in-chrome

IanOvenden commented 6 years ago

Hi,

I've switched on timestamps and run the test again - same results. I couldn't copy timestamps to clipboard, so I'm including as images.:

  1. Page load:

1

  1. Initial drag and drop seemed to work fine:

2a 2b 2c

  1. Attempted to drag second element which didn't complete properly:

3

4: The next time I tried to drag and element, it looked to complete the second failed drag and drop (even though as per previous comments the element had clearly moved):

4a 4b 4c

I hope this helps.

Thanks,

Ian

reppners commented 6 years ago

So to sum this up it seams like the events (and also the logs) are replayed at a later point in time but only if the drag operation is started with a setTimeout(). But the drag image does move, so the code must be executed. Given what kind of optimizations to touch event handling are taking place on mobile it might even does play a role what duration is passed to setTimeout(). I suspect it comes down to when event.preventDefault() is invoked for the initial touchstart/touchmove event and how the browser does handle it then. I'm trying to reach out to the chromium devs because I'm totally lost on how to tackle this.

IanOvenden commented 6 years ago

I've recently received a major update to Android on my Pixel 1...

Android version: 8.1 Chrome version: 63.0.3239.111

Just to confirm, I'm still experiencing this issue. Hopefully the Chromium devs may be able to shed some light on the problem.

Thanks for continuing to investigate.

reppners commented 6 years ago

Yesterday at work had a touch panel device running android 4.4.2 with Firefox Nightly (v59.0a1) and it showed the same behaviour that you are describing! Didn't have time to dig deeper but having a device that exhibits the behaviour might help me to get to the root of this!