Sortable icon indicating copy to clipboard operation
Sortable copied to clipboard

[bug] Continuous item reordering issue in grid layout when dragging the first item to the end of a row

Open wonhyo-e opened this issue 9 months ago • 5 comments

Note: This issue description was drafted with assistance from an AI. Please excuse any formatting inconsistenciesđŸ˜…

Describe the bug When using a grid layout, there's an issue with the first item in each row. When dragging the first item towards the last position in the row, other items in the same row continuously swap positions back and forth, creating a flickering effect. This happens consistently and makes it difficult to place the item in the desired position.

To Reproduce

  1. Create a grid layout with multiple items per row (2 or more columns)
  2. Click and drag the first item in a row
  3. Try to move it to the last position in the same row
  4. Observe how other items in the row continuously swap positions back and forth

Expected behavior Items should maintain stable positions during dragging, and only swap when the dragged item passes the threshold for repositioning. There should be no continuous back-and-forth repositioning of other items.

Information Versions - Look in your package.json for this information: sortablejs = ^1.15.6 @types/sortablejs = ^1

Additional context

  • This issue occurs in the official grid examples as well
  • The problem is reproducible across different browsers and devices
  • The issue is more noticeable on mobile devices with smaller screens

Reproduction This issue can be easily reproduced using the official grid example at https://sortablejs.github.io/Sortable/#grid Steps to reproduce with the official example:

  • Visit https://sortablejs.github.io/Sortable/#grid
  • Reduce your browser window width until the items display in 2-3 columns per row
  • Click and hold on the first item in any row (e.g., item 1)
  • Drag it towards the end position of the same row
  • Observe how other items in the row continuously swap positions back and forth as you approach the end of the row

https://github.com/user-attachments/assets/52ad525c-6ea8-4227-9063-642ae70d6291

wonhyo-e avatar Apr 11 '25 02:04 wonhyo-e

I have been investigating the same issue, referring to the docs for Dealing with swap glitching. I have explicitly set the direction to "horizontal" as recommended, but this doesn't help, at least for me when dragging items of different heights and widths. invertSwap doesn't help that I can see, and swapThreshold: 0.3 doesn't prevent the issue altogether. Investigating alternatives, it seems that the unmaintained https://rosspi.github.io/gridstrap.js/ is slightly less glitchy, and seems to have some debounce/limit code that I cannot find in Sortable.

I tried this and it helped tremendously once I got it working: https://github.com/SortableJS/Sortable/issues/2287

innodonni avatar Apr 14 '25 10:04 innodonni

@innodonni Thank you for your suggestion about using the debounce plugin. I appreciate your effort to help solve this issue. I've implemented and tested the debounce approach as recommended in #2287, but unfortunately, it doesn't address the specific problem I reported.

While the debounce technique is effective at reducing the frequency of sort events during normal dragging operations, it doesn't solve the fundamental issue with the first item in each row. The problem persists because even with debounced sorting, once the sort events do trigger, the same problematic back-and-forth swapping behavior continues to occur when dragging the first item towards the end of a row.

The debounce only delays when the sorting events happen, but doesn't prevent the erratic swapping behavior that makes it difficult to position the first item correctly. The core issue appears to be in how the grid layout algorithm determines positioning for the first item in each row, rather than the frequency of sort events.

I'm still investigating alternative solutions and would welcome any other suggestions. Thanks again for taking the time to respond to my issue report.

https://github.com/user-attachments/assets/1ab29414-7c86-4667-8d23-4152b4440ccd

wonhyo-e avatar Apr 15 '25 11:04 wonhyo-e

I am working on a solution for this issue, I'm thinking at the moment that the logic is similar. If the mouse hasn't moved since the last valid drop target was calculated and a new one has been selected, the new one should be ignored

On Tue, 15 Apr 2025, 12:35 Wonhyo Yi, @.***> wrote:

@innodonni https://github.com/innodonni Thank you for your suggestion about using the debounce plugin. I appreciate your effort to help solve this issue. I've implemented and tested the debounce approach as recommended in #2287 https://github.com/SortableJS/Sortable/issues/2287, but unfortunately, it doesn't address the specific problem I reported.

While the debounce technique is effective at reducing the frequency of sort events during normal dragging operations, it doesn't solve the fundamental issue with the first item in each row. The problem persists because even with debounced sorting, once the sort events do trigger, the same problematic back-and-forth swapping behavior continues to occur when dragging the first item towards the end of a row.

The debounce only delays when the sorting events happen, but doesn't prevent the erratic swapping behavior that makes it difficult to position the first item correctly. The core issue appears to be in how the grid layout algorithm determines positioning for the first item in each row, rather than the frequency of sort events.

I'm still investigating alternative solutions and would welcome any other suggestions. Thanks again for taking the time to respond to my issue report.

— Reply to this email directly, view it on GitHub https://github.com/SortableJS/Sortable/issues/2439#issuecomment-2804745321, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACL552XTC6LKA7P4FCTYI7D2ZTVI5AVCNFSM6AAAAAB25CL4DWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQMBUG42DKMZSGE . You are receiving this because you were mentioned.Message ID: @.***> wonhyo-e left a comment (SortableJS/Sortable#2439) https://github.com/SortableJS/Sortable/issues/2439#issuecomment-2804745321

@innodonni https://github.com/innodonni Thank you for your suggestion about using the debounce plugin. I appreciate your effort to help solve this issue. I've implemented and tested the debounce approach as recommended in #2287 https://github.com/SortableJS/Sortable/issues/2287, but unfortunately, it doesn't address the specific problem I reported.

While the debounce technique is effective at reducing the frequency of sort events during normal dragging operations, it doesn't solve the fundamental issue with the first item in each row. The problem persists because even with debounced sorting, once the sort events do trigger, the same problematic back-and-forth swapping behavior continues to occur when dragging the first item towards the end of a row.

The debounce only delays when the sorting events happen, but doesn't prevent the erratic swapping behavior that makes it difficult to position the first item correctly. The core issue appears to be in how the grid layout algorithm determines positioning for the first item in each row, rather than the frequency of sort events.

I'm still investigating alternative solutions and would welcome any other suggestions. Thanks again for taking the time to respond to my issue report.

— Reply to this email directly, view it on GitHub https://github.com/SortableJS/Sortable/issues/2439#issuecomment-2804745321, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACL552XTC6LKA7P4FCTYI7D2ZTVI5AVCNFSM6AAAAAB25CL4DWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQMBUG42DKMZSGE . You are receiving this because you were mentioned.Message ID: @.***>

innodonni avatar Apr 15 '25 11:04 innodonni

Turns out I was right that editing the debounce plugin to ignore the next drop target would work, but only with my colleagues help did we achieve it. Checking and setting a flag before you trigger changed(); completed(true); and then clearing it when lastX/lastY/lastTime are reinitialised works.

Image

innodonni avatar Apr 15 '25 13:04 innodonni

@innodonni Thank you for your solution! I've implemented the suggested modification to the debounce plugin by setting a flag before triggering changed(); completed(true); and clearing it during reinitialization of lastX/lastY/lastTime. This completely fixed the issue with the first item in each row. I appreciate your help in solving this problem.

https://github.com/user-attachments/assets/2f79c1f6-7eb5-4792-a519-afc784d88973

wonhyo-e avatar Apr 16 '25 01:04 wonhyo-e