06 May 2013

jquery.listSort.js

We have been revamping Prowork. While doing some housekeep over the weekend, I noticed I wrote the [task] drag and sort feature on the current version from scratch. So as part of the housekeep, I converted it to a jQuery plugin - jquery.listSort.js

Why another drag and sort (drop, whatever) plugin?

Back then, I searched for existing plugins (not based on jQuery UI) for the purpose. I found some really good ones but most didn't work for the task list mainly becase the list element contains other collapsible elements (notes, members, dates, etc). Many also break when the list is sorted (by status or created/due dates as we have) or filtered (my task, a team member's task or all tasks). And there is always position issues as most plugins miss the fact that the dragged element may be contained in a parent with a relative position. So using an offset relative to the document (instead of the relative parent) as the absolute position for the clone will always fail. Another one - what happens when you are dragging an element to a position far below the window? Does the window scroll automatically or you are just left hanging there at the bottom of the current view?

Long story short, I had to write one.

The plugin has the following options (all optional):

  • dragElement
    You may want to have an element within the list as your drag anchor. Don't forget to set the CSS property for the element though (cursor: move). The plugin doesn't do that for you. If this is not set, it means you can drag the whole element.

  • cloneClass
    CSS class to add to the cloned copy of the element.

  • dropClass
    CSS class to add to the element at the location the currently dragged element can be dropped.

  • customClone
    By default, the plugin clones the whole element. Sometimes, we want to clone just a part of the element. The plugin sends the element being dragged as an argument to this function. Simply return your custom clone object. At Prowork for example, the only thing I need is the task title.

    $('li.task').listSort({
      'customClone': function ($obj) {
        return $('<div>'+$('p.task-title', $obj).html()+'</div>');
      }
    });

  • onStartDrag
    If you need to do some things before the drag starts, here is your cue.

    $('li.task').listSort({
      'onStartDrag': function () {
        // Collapse all 'opened' tasks
    
        $('li.dropdown').each(function() {
          $('.taskdrop', this).hide('fast');
          $(this).removeClass('dropdown');
        });
      }
    });

  • onEndDrag
    Ok, we are done, what's next? Save the new order to db maybe?

    $('li.task').listSort({
      'onEndDrag': function (order) {
        var data = $(order).map(function() {
          return $(this).attr('id').match(/task\-(.+)$/)[1];
    	}).get();
        $.post("path/to/ajax/server/sort-list", { "ids[]": data });
      }
    });

Demo?

Try re-sort the [plugin] options I mentioned above by dragging the title. Here is the setup:

$(function() {
  $('ul.sort li').listSort({
    'dragElement':'b',
    'dropClass':'hit',
    'onEndDrag': function (order) {
      var data = $(order).map(function() {
	    return $('b', $(this)).text();
	  }).get();
      console.log(data);
    }
  });
});

On a final note, I removed and added a couple of things when converting to a jQuery plugin. If somethings don't work, or you have issues somewhere, submit an issue or let me know. Plus be free to fork, iterate and pull.

My name is Opeyemi Obembe. I build things for web and mobile. You should follow me on Twitter (@kehers).