-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Sortable v1.0 — New capabilities
Sortable.js is a minimalistic library for modern browsers and touch devices that doesn't require jQuery. As you may have guessed from its name, the library was developed for sorting elements by means of drag’n’drop. Standard solution in this case is jQuery UI/Sortable, and that’s 64kB + 10kB, neither more and no less. The result is 75kB of gzipped in a project, where jQuery is not used at all. Quite recently, there was another article at Habrahabr related to implementation of a similar functionality, but again with jQuery (touch devices are not supported by the suggested solution either). Apart from problems with weight, all libraries that I’ve found were unable to work with a dynamically varying list. At the moment of plug-in initialization, they were defining positions of all elements; to refresh them, we had to reinitialize the plug-in, or to call $(‘...’).sortable(‘refresh’) method, which is quite inconvenient. Since my task didn’t require supporting old browsers, I’ve tried to create the functionality I needed in pure JS with the use of HTML5 Drag’n’Drop. After reading the related articles, it turned out that today it is very simple to create such functionality, it can even be made with 25 lines only (without commentaries and spacing): http://jsfiddle.net/RubaXa/zLq5J/
// Making all siblings movable // Function responsible for sorting // Sorting // End of sorting // Notification about the end of sorting // Sorting starts // Remembering an element that will be moved // Limiting the movement type // Subscribing to the events at dnd // If this action is performed withour setTimeout, then // the moved object will be of this class. // Using
As it may be noticed from the code, the whole sorting process consists of simple movement of the dragged element by means of rootEl.insertBefore(dragEl, target.nextSibling || target), where target is an element that was targeted. If you have already tested the example, you must have noticed that it is impossible to move an element to the first position. One more peculiarity of this method is that onUpdate is called each time, even if the element was not moved. In order to fix the first problem, all we have to do is to add testing during sorting. It is necessary to insert an element after target.nextSibling only in case it is not the first element of the list: http://jsfiddle.net/RubaXa/zLq5J/3/
// Sorting
Besides that, simply saving a link to the next element (nextEl = dragEl.nextSibling) in the moment dragstart allows us to get rid of the second problem (http://jsfiddle.net/RubaXa/zLq5J/4/, lines 29 and 38). On the face of it, everything looks fine, we have a compact and intelligible code that is supported by the majority of browsers, and if we add the support of attachEvent and remove dragEl.classList.add/remove, then the code will work even in IE5.5 :] But if we change the example a little bit by simply increasing height of list elements, we will have a third problem. Sorting works fine from the top downward, but it works poorly from the bottom upwards. That’s why we need to rewrite the logic of element inserting «before» or «after» so that it would consider, in which half the mouse cursor is located (upper or lower). For this purpose, we acquire element coordinates against the screen at onDragOver and check, in which half the cursor is located: http://jsfiddle.net/RubaXa/zLq5J/6/
Touch support
Unfortunately, drag’n’drop doesn’t work on touch devices. That’s why we needed to create some sort of emulation based on touch-events. I have been scratching my had over this for a long time, read documentation, but never found an answer. Finally, after digging a little bit more, I remembered one more excellent method document.elementFromPoint, which allows to obtain a link to an element by coordinates. As the result, I clone the element that will play the role of a “ghost” under the finger at touchstart, and move it by means of translate3d at touchmove:
Besides that, I initiate setInterval, where I check the current element under the finger every 100ms:
// Hiding a “ghost” under the finger // Obtaining an element under the finger // Checking the obtained element, and if it belongs to rootEl, // we call onDragOver method: // Showing the “ghost” again
That’s it, as you can see, there’s nothing supernatural. Now, we need to draw up a code, write some documentation, and the micro library is ready. Sortable
The library turned out to weight 2kB gzipped and has the following capabilities: • Sorting of vertical and horizontal lists; • Ability to set the elements to be sorted (css-selector); • Combining into groups; • Ability to set handle (an element that can be dragged); • A class that is added to the moved element; • onAdd, onUpdate, onRemove events; • Working with dynamically varied lists. Code example:
// Simple list, e.g. ul > li // Grouping // handle + event // css-selector, which can be used to drag // css-selector of elements, which can be sorted // a link to an element that was moved
Today, only basic functionality is available. I would be glad to receive any feedback or pull request, thanks for your attention.