A flexible, opinionated sorting plugin for jQuery
View on GitHub Download (v0.9.10)
Download minified version (8.5 kb, gzipped ~2.8 kb)
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.
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.
jquery-sortable.js
has been tested with the following browsers
If you confirmed, that it works on other browsers please tell me .
Heads Up! There is no on-the-fly creation of sublists. Only list items that contain a sublist are drop targets.
Making a list sortable consists of 3 easy steps
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
<ol class='example'> <li>First</li> <li>Second</li> <li>Third</li> </ol> <script src='js/jquery-sortable.js'></script>
$(function () { $("ol.example").sortable() })
var adjustment $("ol.simple_with_animation").sortable({ group: 'simple_with_animation', pullPlaceholder: false, // animation on drop 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) }) }, // set item relative to cursor position onDragStart: function ($item, container, _super) { var offset = $item.offset(), pointer = container.rootGroup.pointer adjustment = { left: pointer.left - offset.left, top: pointer.top - offset.top } _super($item, container) }, onDrag: function ($item, position) { $item.css({ left: position.left - adjustment.left, top: position.top - adjustment.top }) } })
group
$("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 })
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) })
var group = $("ol.limited_drop_targets").sortable({ group: 'limited_drop_targets', isValidTarget: function (item, container) { if(item.is(".highlight")) return true else { return item.parent("ol")[0] == container.el[0] } }, onDrop: function (item, container, _super) { $('#serialize_output').text(group.sortable("serialize").get().join("\n")) _super(item, container) }, serialize: function (parent, children, isContainer) { return isContainer ? children.join() : parent.text() }, tolerance: 6, distance: 10 })
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' })
// 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) } })
placeholder
| 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 |
| 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 |
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
.sortable("serialize")
Serialize all selected containers. Returns a
jQuery
object . Use
.get()
to retrieve the array, if needed.
| 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 |
distance
|
0 |
Distance the mouse has to travel to start dragging |
handle
|
"" |
The css selector of the drag handle |
itemSelector
|
"li" |
The css selector of the items |
isValidTarget
|
function ($item, container) { return true } |
Check if the dragged item may be inside the container. Use with care, since the search for a valid container entails a depth first search and may be quite expensive. |
onCancel
|
function ($item, container, _super) { } |
Executed before onDrop if placeholder is detached. This happens if pullPlaceholder is set to false and the drop occurs outside a container. |
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").removeAttr("style") $("body").removeClass("dragging") } |
Called when the mouse button is beeing released |
onMousedown
|
function($item, event, _super) { event.preventDefault() } |
Called on mousedown. |
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. |
serialize
|
function ($parent, $children, parentIsContainer) { var result = $.extend({}, $parent.data()) if(parentIsContainer) return $children else if ($children[0]){ result.children = $children delete result.subContainer } |
Specifies serialization of the container group. The pair $parent/$children is either container/items or item/subcontainers. Note that this default method only works, if every item only has one subcontainer |
tolerance
|
0 |
Set tolerance while dragging. Positive values will decrease sensitivity. |
| 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