Issue: Smooth Scrolling & Drag-and-Drop Not Working Properly on Mobile (SortableJS)
I am experiencing issues with SortableJS when implementing drag-and-drop functionality in my project. The main issues are:
- 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.
- 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
- Implement SortableJS in a mobile-responsive grid-based layout.
- Add drag-and-drop functionality with scroll: true, bubbleScroll: true, scrollSensitivity: 100, and scrollSpeed: 10.
- Test on mobile devices: scrolling is slow and unresponsive.
- 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:
- How to improve scrolling performance on mobile devices?
- Why drag-and-drop is not working on the first attempt on web browsers?
Thank you in advance! 🙌
I'm having the same issue and it's happening in safari.
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>