Sortable icon indicating copy to clipboard operation
Sortable copied to clipboard

Incorrect returning animation

Open kelvinkoko opened this issue 4 years ago • 6 comments

Reproduction

In the "Simple list" example https://sortablejs.github.io/sortablejs/

Steps to Reproduce

Summary of steps if applicable.

  1. Drag item 1 to bottom
  2. Keep drag state move mouse out of the list
  3. Release drag
  4. The item 1 "return to list animation" is return to the top (original item 1 position) instead of bottom (the new position)

Describe the bug

Return to list animation is incorrect

Expected behavior

The animation should return item to new position

Version

Lastest version using in the sample page

Additional Context

return-animation

kelvinkoko avatar Nov 28 '20 04:11 kelvinkoko

Same here. Any updates with this bug?

hvab avatar May 09 '21 13:05 hvab

This is affecting us as well and the incorrect animation ends up confusing the user. Not a good user experience :(

danline avatar Aug 22 '21 10:08 danline

If there is a way to disable the animation, that would be an acceptable workaround too but there is no way to disable the animation either.

danline avatar Aug 22 '21 10:08 danline

For future visistors & googlers (like me):

The reason for this issue is how the browser-native drag&drop API handles ghost images. There are two ways to fix this.

Fix 1: Disable native drag&drop altogether

This can be done by passing the forceFallback: true option to a Sortable instance. The fallback is probably still around from times before browsers implemented native drag&drop (which is a long time ago). Note that I'm not aware whether enforcing the fallback implementation will somehow degrade Sortable.js' performance or reliability.

Fix 2: Replace the drag image with something transparent

This can be done during the browser's dragstart event, which can only be accessed through a plugin in Sortable.js. (Sortable's own dragstart event gives access to the browser-native event, but fires too late to still be able to replace the drag image.)

Below is a plugin that will do so. It has to be mounted by running Sortable.mount(NoDragImagePlugin). When mounted, it can be activated in any Sortable.js instance by passing a noDragImage: true option.

See the plugin in action in this Codepen.

class NoDragImagePlugin {
  static pluginName = "noDragImage";

  // We use a blank canvas as the replacement drag image
  static _blankCanvas = document.createElement("canvas");

  static {
    // Make the canvas as inaccessible as possible...
    this._blankCanvas.setAttribute("aria-hidden", "true");
    this._blankCanvas.width = 1;
    this._blankCanvas.height = 1;
    this._blankCanvas.style.opacity = "0";
    this._blankCanvas.style.position = "absolute";
    this._blankCanvas.style.pointerEvents = "none";

    // ...because we actually need to inject the placeholder somewhere
    // in the DOM, or otherwise Chrome will show a fallback image
    document.body.appendChild(this._blankCanvas);
  }

  dragStart(event) {
    event.originalEvent.dataTransfer.setDragImage(
      NoDragImagePlugin._blankCanvas,
      0,
      0
    );
  }
}

loilo avatar Sep 26 '23 09:09 loilo

Alternative solution here is to make any place on the document to accept the drop (sortable handles it anyway just don't mark it as accepted for the browser).

To do this we can simply listen dragover and call preventDefault() on its event.

document.addEventListener('dragover', (event) => {
  event.preventDefault()
})

I got this workaround after the following reading: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#performing_a_drop

I'm not yet sure about the circumstances of doing so, but sharing with you just in case ;).

ezhlobo avatar Oct 11 '23 13:10 ezhlobo

@loilo you are awesome...... thank you

9mm avatar Jan 03 '24 01:01 9mm