react-dnd / react-dnd

Drag and Drop for React
http://react-dnd.github.io/react-dnd
MIT License
20.98k stars 1.99k forks source link

modifying styles during dragstart handler causes Chrome to fire dragend immediately on start #1085

Closed cormacrelf closed 5 years ago

cormacrelf commented 6 years ago

EDIT: not actually unique to captureDraggingState. It's for any CSS change applied while dragstart is still being handled.

https://bugs.chromium.org/p/chromium/issues/detail?id=168544

Workaround

Make any changes that cause style changes (e.g. firing actions that cause a class to be applied...) in a setTimeout(() => {}, 0) wrapper.

Describe the bug

Usually with the HTML5 backend, one 'dragstart' event fires when you drag something, and one 'dragend' fires when you drop it.

Now, in Chrome, at least in v67, when you have captureDraggingState: true, this is messed up. It goes:

Because of this, you can't actually drag anything -- it is immediately dropped. Workaround is to drop IE11 DragLayer support by using the default value, false.

To Reproduce Steps to reproduce the behavior:

  1. On any example drag source, run connectDragPreviewwith{ captureDraggingState: true }` as its options.
  2. Using Chrome dev tools, run monitorEvents(document.body, 'dragstart') and monitorEvents(document.body, 'dragend').
  3. Try to drag the source.
  4. Watch the console output.
  5. Also, observe that nothing happens under your mouse cursor, the drag barely happens at all.

Expected behavior

There should be no 'dragend' until you drop, and you should be able to drag things.

Desktop (please complete the following information):

It works fine in Firefox 60 on Windows.

Additional context

Technically I am using angular-skyhook, but the code responsible for passing an Element/Node through to a backend with options is almost identical, even slightly simpler with no need for wrapConnectorHooks:

Pavel910 commented 6 years ago

@cormacrelf I ran into this problem today, in my case I have a list of draggable items (like a drawer with items), and when I start dragging I hide the drawer via CSS - at this point dragend fires and breaks dragging.

I managed to bypass this behavior by using setTimeout to update CSS. It's an ugly hack but solved my problem until a real fix lands.

neoimale commented 5 years ago

Same problem here.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

jimsleon commented 5 years ago

I encountered same problem and it doesn't happen all the time when you change CSS in dragstart, it looks like changing CSS which involves element size change will cause this to happen.

cmzuluagam commented 4 years ago

I was having the same issue, and was able to workaround it by using the setTimeout(() => {}, 0) approach described above. I also modified the z-index of the DragLayer component so it has a lower index that the Dragged component and thus avoid the drangend event to fire

iCyris commented 4 years ago

Same problem, the workaround works well, thanks.

Chetan11-dev commented 3 years ago

Code Example

   useEffect(() => {
        if (isDragging) {
            setTimeout(() => {
                ref.current!.style.display = 'none'
            }, 0)
        }
    }, [isDragging])

    return (
        <div ref={ref} className={classNames('side-panel left-0', Classes['left-side-panel'])}>
            {ChildrenComponent}
        </div>
    )
raphaelsantos commented 2 years ago

Ran into same issue when dragging a tiny element containing a single character. So far I could only reproduce it when the character inside was "I" (apparently due to it not being wide enough).