Sortable icon indicating copy to clipboard operation
Sortable copied to clipboard

Issue: Smooth Scrolling & Drag-and-Drop Not Working Properly on Mobile (SortableJS)

Open maulikjoshi001 opened this issue 10 months ago • 3 comments

I am experiencing issues with SortableJS when implementing drag-and-drop functionality in my project. The main issues are:

  1. Mobile Scrolling Issue: On mobile devices, the scrolling is very slow and laggy when interacting with the list. The scroll sensitivity does not seem to be effective despite adjusting scrollSensitivity and scrollSpeed properties.
  2. Drag-and-Drop First Interaction Issue (Web): On web browsers, the drag-and-drop does not work properly on the first attempt. However, after the initial interaction, it starts working as expected. Expected Behavior
  • Smooth scrolling on mobile devices when dragging items inside the list.
  • Drag-and-drop should work on the first attempt in web browsers.

Current Behavior

  • Scrolling on mobile is slow and laggy.
  • Dragging does not work initially on the web but starts working after interacting with the list.

Steps to Reproduce

  1. Implement SortableJS in a mobile-responsive grid-based layout.
  2. Add drag-and-drop functionality with scroll: true, bubbleScroll: true, scrollSensitivity: 100, and scrollSpeed: 10.
  3. Test on mobile devices: scrolling is slow and unresponsive.
  4. Test on web browsers: dragging does not work on the first attempt.

Code Example Here is the relevant code snippet used for SortableJS:

const sortableRef = useRef(null);

useEffect(() => {
    if (sortableRef.current) {
        if (sortableInstance) sortableInstance.destroy();

        const newSortable = Sortable.create(sortableRef.current, {
            ghostClass: "dragging",
            scroll: true,
            scrollSensitivity: 100,
            scrollSpeed: 10,
            bubbleScroll: true,
            handle: ".drag-handle",
            onEnd: (evt) => {
                if (evt.oldIndex === evt.newIndex) return;
                const reorderedWishes = [...allWishesDetails];
                const movedItem = reorderedWishes[evt.oldIndex];
                reorderedWishes.splice(evt.oldIndex, 1);
                reorderedWishes.splice(evt.newIndex, 0, movedItem);
                dispatch(setAllWishesDetails(reorderedWishes));
            }
        });

        setSortableInstance(newSortable);
    }
}, [allWishesDetails, dispatch]);

Potential Solutions Tried

  • Adjusted scrollSensitivity, scrollSpeed, forceFallback, and bubbleScroll properties.
  • Checked CSS to identify conflicting styles.
  • Verified event handling for drag-and-drop initialization.

Request for Help Would appreciate any insights on:

  1. How to improve scrolling performance on mobile devices?
  2. Why drag-and-drop is not working on the first attempt on web browsers?

Thank you in advance! 🙌

maulikjoshi001 avatar Mar 06 '25 14:03 maulikjoshi001

I'm having the same issue and it's happening in safari.

moonspam avatar Mar 07 '25 05:03 moonspam

Findings and Fixes

I managed to fix a few dragging issues on mobile by doing the below. I can also now smooth scroll past the draggable items and hold down on them to enable dragging.

Biggest Fix: Remove any CSS transitions on the draggable elements:

On all my clickable elements i have a class that makes the element shrink a little when you click on it, its a css transform to give the user visible feedback of their action. This was on my draggable tile and made dragging not work most of the time on mobile. I assume its because css is transitioning while you are trying to drag and something faults there. Remove all css transitions from your draggable tiles.

Add draggable false to images:

Because your phone wants to do its natural behaviour on an image you will want to prevent that. I noticed adding draggable false and preventing the context menu has made dragging smoother. Code snippet in the code example at the end.

Ensure unique sortable IDs

I am using svelte and i have a reusable gallery component that allows sorting of gallery items. This component is sometimes used more than once on a page and I had the sortable element hard coded as document.getElementById("gallery-sortable") which if there is more than one gallery on a page you can see an issue is going to happen. I change to using refs instead.

Lastly here is my sortable config

This is my svelte onMount so pick from this what you want

<script>
  onMount(() => {
    const sortEl = ref
    if (!sortEl) return;

    // https://github.com/SortableJS/Sortable
    var sortable = Sortable.create(sortEl, {
      ghostClass: "sortable-ghost",  // Class name for the drop placeholder
      chosenClass: "sortable-chosen",  // Class name for the chosen item
      dragClass: "sortable-drag",  // Class name for the dragging item
      handle: ".sortable-tile",
      filter: ".sortable-ignore",
      delayOnTouchOnly: true,
      delay: 100,
      bubbleScroll: true,
      scroll: true,
      scrollSensitivity: 100,
      scrollSpeed: 10,
      onEnd: async function (evt) {
        evt.stopPropagation();
        if (evt.type !== "end") return;
        if (evt.oldIndex == undefined || evt.newIndex == undefined) return;
        if (evt.oldIndex == evt.newIndex) return;

        // do some server request
        // await myserver.sort(evt.oldIndex, evt.newIndex)
      },
    });

    return () => {
      sortable.destroy()
    }
  })
</script>

<!-- html -->
<div class="grid grid-cols-2 gap-3">
  <div class="item">
      <img
        src="somesrc.jpg"
        draggable="false"
        oncontextmenu={(e) => e.preventDefault()}
        ontouchstart={(e) => e.preventDefault()}
      />
  </div>
</div>

gregg-cbs avatar Apr 12 '25 11:04 gregg-cbs