Sortable v1.0: New Features

    Hi Habr! On the eve of the new year I want to share my joy - the release of Sortable v1.0 . Exactly a year ago, I presented to your court my little tool for sorting a list using drag'n'drop. All this time I meticulously collected feedback, added new features and rules for minor bugs. Under the cut, I’ll talk about new features, integration with AngularJS , Meteor and other nuances.


    Sortable is a minimalistic tool for organizing sorting within a list and between lists. The library is independent of jQuery or other solutions, uses the Native Drag'n'Drop API, works on both desktop and touch devices. It has a simple API, integrates easily into the project and is a great replacement for jQueryUI / Sortable ;]

    And so, what's new about this release:

    • Advanced groups (flexible movement settings and more)
    • Move animation
    • Smart scrolling browser window and list
    • Disabling sorting (allows emitting draggable and droppable)
    • Methods for getting and changing sorting
    • Filtering ability
    • Support for AngularJS and Meteor

    Advanced groups

    From the very beginning, the library had the ability to move between groups, it was enough to give them the same name and you're done:
    // foo и bar — ссылки на HTMLElement
    Sortable.create(foo, { group: 'shared', });
    Sortable.create(bar, { group: 'shared' });
    Work example (gif)

    Over time, it became apparent that this was not enough. For example, it was impossible to make so that one list only gave, and another only accepted elements. In addition, the key drawback was the lack of the ability to somehow organize the interaction of several groups. It was necessary to work out a solution that would allow realizing the arisen tasks and leaving the possibility of expansion for the future, while not losing the current interface.

    Now you can set the `group` option as an object with the following properties:
    • name - name of the group;
    • pull - the ability to "pull out" elements when moving between lists, the same property can take the value of `clone`;
    • put - the ability to accept an element from another group, or an array of allowed groups.

    How it works is easier to explain with an example:
    • You have three lists “A”, “B” and “C”;
    • You need to drag from “A” and “B” to “C”, between “A” and “B” transfer is not possible;
    • When dragging from "A", a "clone" should appear in its place.

    To show all the possibilities at once, I will solve this problem in two ways.
    General groupSeveral groups
    Sortable.create(listA, {
      group: {
        name: 'shared',
        pull: 'clone',
        put: false
    Sortable.create(listB, {
      group: {
        name: 'shared',
        put: false
    Sortable.create(listC, {
      group: 'shared'
    Sortable.create(listA, {
      group: {
        name: 'A',
        pull: 'clone'
    Sortable.create(listB, {
      group: 'B'
    Sortable.create(listC, {
      group: {
        put: ['A', 'B']


    There is nothing special to paint here, the animation is very simple, using CSS3 transition, you can enable it by setting the option `animation` in` ms`. Alas, it has flaws that have not yet been resolved, but I hope in the future there will be a way how to "cheaply" fix them.
    Work example (gif)

    Smart scrolling window and list

    Most recently, a task arose: to scroll the window when one of the edges was reached. In theory, this should have worked by default, as The Native Drag'n'Drop API is used, but in reality the browser was extremely reluctant to scroll the window. There was also a problem if the list is in overflow. Therefore, thinking, it turned out to do a smart scroll, which first scrolled the list if it was in overflow and / or a window if we reached the edge of the browser. For more fine-tuning, three additional options are introduced:
    • scroll - enable auto-scroll;
    • scrollSensitivity - how much you need to get closer to the edge to activate scrolling;
    • scrollSpeed - scroll speed in `px`;


    Disabling sorting

    Yes, exactly, it may seem strange, but with this parameter you can disable what this tool was created for;] For example, you can use it to simulate draggable and droppable: ? html, js, output

    Methods for getting and changing sorting

    Since this library appeared as a result of researching the capabilities of the Drag'n'Drop API, the trivial methods to get order or change it simply did not work. Having looked at the jQueryUI API, I found that they can only get the order of the elements, but they cannot be changed, this is not the order;] To solve all these problems, the `store` property was added, which accepts an object from the two parameters` get` and ` set` for receiving and saving sorting, as well as two methods `toArray` and` sort`.

    For example, maintaining order through `localStorage` would look like this:
    Sortable.create(users, {
      store: {
        // Получение сортировки (вызывается при инициализации)
        get: function (sortable) {
          var order = localStorage.getItem(;
          return order ? order.split('|') : [];
        // Сохранение сортировки (вызывается каждый раз при её изменении)
        set: function (sortable) {
          var order = sortable.toArray();
          localStorage.setItem(, order.join('|'));
    }); (reorder and refresh the page)

    Filtering ability

    Suppose you need to make a sortable list with the ability to edit and delete an item. Previously, you would need to independently attach the necessary handlers. Now, you can solve a similar problem using the library itself, without additional tools:,js,output

    AngularJS Support

    Angular is gaining more and more market share and to make it easier for people to use Sortable, it was decided to make a directive for quick integration into the project. Looking at the analogs, I saw a strange thing, all as one do this:
    • {{ item }}

    What for? After all, this is simply copy-paste, and to be completely honest, a crutch. In my opinion, the following entry will be logical and correct, without `ng-model`:
    • {{ item }}

    The data of interest to us already contains `ng-repeat`, to get them we need the` $ parse` function and a little trick. The trick is that the data from `ng-repeat` can only be obtained by finding the spec. comment left by the angular himself:

      Now we can create a method for working with data related to `ng-repeat`:
       * Получить объект для работы с данными в ng-repeat
       * @param {HTMLElement}  el
       * @returns {object}
      function getNgRepeat(el) {
        // Получаем текущий `scope` связанный в элементом
        var scope = angular.element(el).scope();
        // Находим нужный нам комментарий
        var ngRepeat = [], function (node) {
           return (
                 (node.nodeType === 8) &&
                 (node.nodeValue.indexOf('ngRepeat:') !== -1)
        // Прасим название переменных элемента и массива
        ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*([^\s]+)\s+in\s+([^\s|]+)/);
        // Конвертируем названия переменных в `expression` для получения их значений из `scope`
        var itemExpr = $parse(ngRepeat[1]);
        var itemsExpr = $parse(ngRepeat[2]);
        return {
           // Получить модель элемента списка
           item: function (el) {
              return itemExpr(angular.element(el).scope());
           // Получить массив связанный с `ng-repeat`
           items: function () {
              return itemsExpr(scope);

      Work example:
      Full directive code:

      Integration with Meteor

      This is a completely new feature, which appeared thanks to Dan Dascalescu , so if you use meteor, the library is added to the atmosphere , and Dan added a detailed manual for use and an example . If anything, put a task with a meteor tag on it, he will be happy to help;]

      In conclusion, I want to say thanks to all those who participated in the testing and development of the library, even though it got a little “fat”, but it's still light and flexible intrument. Thanks for attention.

      Future plans

      • Coverage with tests (it’s not yet completely clear how to cover Drag'n'Drop, but there are ideas)
      • Enhanced Animation
      • Extension system (for example, nested lists or combining two elements into one)
      • Axis limitation (alas, you may have to abandon the Drag'n'Drop API)
      • Your option ;]

                     Examples     |    Code and Documentation     |    My github     |    @ibnRubaXa

      Also popular now: