jQuery Sortable

A flexible, opinionated sorting plugin for jQuery

View on GitHub Download (v0.9.6)

Download minified version (7.3 kb)

So what does it do?

  • Sorts any items in any container
  • Fully supports nested containers
  • Connect lists
  • Callbacks and events (see docs)
  • Pure drag/drop lists
  • Vertical and horizontal sorting

Why another sortable plugin?

you might ask. Aren't there many others?

The answer is: nested lists. None of the other solutions had a decent support for nested lists. nestedSortable relies on a fixed width hierarchy. Others mimic the way jQuery UI does sortables and therefore require ugly hacks that suffer from sudden jumps.

The opinionated part

This plugin does one and only one thing: sorting . If you need animations or autoscrolling, do them yourself .

Moreover this plugin assumes that the placeholder has zero height/width. As a result, the item dimensions may be cached. This might change in the future, if need be.

Compatibility

jquery-sortable.js has been tested with the following browsers

  • Firefox >= 3.5
  • Chrome
  • Opera
  • Konqueror
  • IE > 7

If you confirmed, that it works on other browsers please tell me .

Show it to me! With default options.

Heads Up! There is no on-the-fly creation of sublists. Only list items that contain a sublist are drop targets.

  1. First
  2. Second
  3. Third
    1. First
    2. Second
    3. Third
      1. First
      2. Second
      1. First
      2. Second
  4. Fourth
  5. Fifth
  6. Sixth

Making a list sortable consists of 3 easy steps

Add styles

body.dragging, body.dragging * {
  cursor: move !important;
}

.dragged {
  position: absolute;
  opacity: 0.5;
  z-index: 2000;
}

ol.example li.placeholder {
  position: relative;
  /** More li styles **/
}
ol.example li.placeholder:before {
  position: absolute;
  /** Define arrowhead **/
}

Look here for a complete example

Define your HTML

<ol class='example'>
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
</ol>
<script src='js/jquery-sortable.js'></script>

Call the initializer

$(function  () {
  $("ol.example").sortable()
})

Connected lists with drop animation

$("ol.simple_with_animation").sortable({
  group: 'simple_with_animation',
  pullPlaceholder: false,
  onDrop: function  (item, targetContainer, _super) {
    var clonedItem = $('<li/>').css({height: 0})
    item.before(clonedItem)
    clonedItem.animate({'height': item.height()})
    
    item.animate(clonedItem.position(), function  () {
      clonedItem.detach()
      _super(item)
    })
  }
})
  • Define your own drop animation
  • Connect lists by placing them in the same group
  • Only sort if the cursor is above a container

show me the code

  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
  5. Item 5
  6. Item 6
  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
  5. Item 5
  6. Item 6

Sort handle and limited drag/drop

$("ol.simple_with_drop").sortable({
  group: 'no-drop',
  handle: 'i.icon-move',
  onDragStart: function (item, container, _super) {
    // Duplicate items of the no drop area
    if(!container.options.drop)
      item.clone().insertAfter(item)
    _super(item)
  }
})
$("ol.simple_with_no_drop").sortable({
  group: 'no-drop',
  drop: false
})
$("ol.simple_with_no_drag").sortable({
  group: 'no-drop',
  drag: false
})
  • Drag the items by a handle
  • Pure drag/drop container
  • Clone items on drag

show me the code

I'm draggable and droppable

  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
  5. Item 5
  6. Item 6

I'm only draggable

  1. Item 1
  2. Item 2
  3. Item 3

I'm only droppable

  1. Item 1
  2. Item 2
  3. Item 3

Toggable nested lists

var oldContainer
$("ol.nested_with_switch").sortable({
  group: 'nested',
  afterMove: function (placeholder, container) {
    if(oldContainer != container){
      if(oldContainer)
        oldContainer.el.removeClass("active")
      container.el.addClass("active")
      
      oldContainer = container
    }
  },
  onDrop: function (item, container, _super) {
    container.el.removeClass("active")
    _super(item)
  }
})

$(".switch-container").on("click", ".switch", function  (e) {
  var method = $(this).hasClass("active") ? "enable" : "disable"
  $(e.delegateTarget).next().sortable(method)
})
  • Nest lists of arbitrary depth
  • Highlight the current container
  • Enable/disable lists

show me the code

  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
    1. Item 3.1
    2. Item 3.2
    3. Item 3.3
    4. Item 3.4
    5. Item 3.5
    6. Item 3.6
  5. Item 5
  6. Item 6
  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
    1. Item 3.1
    2. Item 3.2
    3. Item 3.3
    4. Item 3.4
    5. Item 3.5
    6. Item 3.6
  5. Item 5
  6. Item 6

Sorting a bootstrap menu

  • Sort vertically
  • Define the nested containers differently
  • Exclude some items from being sortable

Heads Up! The itemSelector should always match every sibling of any item. If you want to exclude some items, use the exclude option. See the first example here why this is a good idea.

$("ol.nav").sortable({
  group: 'nav',
  nested: false,
  vertical: false,
  exclude: '.divider-vertical'
})
$("ol.dropdown-menu").sortable({
  group: 'nav'
})

Sort tables (doesn't work well in Konqueror and IE)

// Sortable rows
$('.sorted_table').sortable({
  containerSelector: 'tbody',
  itemSelector: 'tr',
  placeholder: '<tr class="placeholder"/>'
})

// Sortable column heads
var oldIndex
$('.sorted_head tr').sortable({
  containerSelector: 'tr',
  itemSelector: 'th',
  placeholder: '<th class="placeholder"/>',
  vertical: false,
  onDragStart: function (item, group, _super) {
    oldIndex = item.index()
    item.appendTo(item.parent())
    _super(item)
  },
  onDrop: function  (item, container, _super) {
    var field,
    newIndex = item.index()
    
    if(newIndex != oldIndex)
      item.closest('table').find('tbody tr').each(function (i, row) {
        row = $(row)
        field = row.children().eq(oldIndex)
        if(newIndex)
          field.before(row.children()[newIndex])
        else
          row.prepend(field)
      })

    _super(item)
  }
})
  • Sort tables
  • Specify custom placeholder
  • Provide custom callbacks, to change the sorting behaviour

show me the code

Sortable Rows

A Column B Column
A Item 1 B Item 1
A Item 2 B Item 2
A Item 3 B Item 3
A Item 4 B Item 4
A Item 5 B Item 5
A Item 6 B Item 6

Sortable column heads

A Column B Column
A Item 1 B Item 1
A Item 2 B Item 2
A Item 3 B Item 3
A Item 4 B Item 4
A Item 5 B Item 5
A Item 6 B Item 6

jQuery API

The sortable() method must be invoked on valid containers, meaning they must match the containerSelector option.

.sortable([options])

Instantiate sortable on each matched element. The available options are divided into group options and container options .

Group options are shared between all member containers and are set on the first instantiation of a member container. Subsequent instantiations of further containers in the same group do not change the group options.

Container options can be set seperately for each member of a group.

.sortable("enable")

Enable all instantiated sortables in the set of matched elements

.sortable("disable")

Disable all instantiated sortables in the set of matched elements

Group options

Option Default Description
afterMove
function (placeholder, container) {
}
This is executed after the placeholder has been moved.
containerSelector
"ol, ul"
The css selector of the containers
handle
""
The css selector of the drag handle
itemSelector
"li"
The css selector of the items
onDrag
function (item, position, _super) {
  item.css(position)
}
Executed at the beginning of a mouse move event. The Placeholder has not been moved yet
onDragStart
function (item, container, _super) {
  item.css({
    height: item.height(),
    width: item.width()
  })
  item.addClass("dragged")
  $("body").addClass("dragging")
}
Called after the drag has been started, that is the mouse button is beeing held down and the mouse is moving. The container is the closest initialized container. Therefore it might not be the container, that actually contains the item.
onDrop
function  (item, container, _super) {
  item.removeClass("dragged").attr("style","")
  $("body").removeClass("dragging")
}
Called when the mouse button is beeing released
placeholder
'<li class="placeholder"/>'
Template for the placeholder. Can be any valid jQuery input e.g. a string, a DOM element
pullPlaceholder
true
If true, the position of the placeholder is calculated on every mousemove. If false, it is only calculated when the mouse is above a container.

Container options

Option Default Description
drag
true
If true, items can be dragged from this container
drop
true
If true, items can be droped onto this container
exclude
""
Exclude items from being draggable, if the selector matches the item
nested
true
If true, search for nested containers within an item
vertical
true
If true, the items are assumed to be arranged vertically

Listed in alphabetical order