SortableJS
Turning Chaos into Click-and-Drag Order!
Sortable.js is a powerful JavaScript library that enables drag-and-drop interactions for reordering elements within lists and grids. It provides a smooth, intuitive way for users to reorganize content with minimal code implementation.
- Drag and drop sorting for lists, grids, and nested structures
- Touch device support for mobile-friendly interactions
- Cross-list sorting capabilities
- Animation effects during sorting operations
- Customizable drag handles
- Event hooks for complete control over the sorting process
- Support for multiple item selection and manipulation
Installation
Include Sortable.js in your project by adding the script to your HTML file:
<script src="path/to/sortable.js"</script>
Initialize Sortable on any container element that holds sortable items:
// Basic initialization
new Sortable(document.getElementById('list'), {
// options
});
HTML Structure
Sortable.js works with virtually any HTML structure. The most common pattern is a list of items:
<!-- Simple list -->
<ul id="simple-list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
<!-- Grid layout -->
<div id="grid-demo" class="row">
<div class="col-3">Item 1</div>
<div class="col-3">Item 2</div>
<div class="col-3">Item 3</div>
<div class="col-3">Item 4</div>
</div>
<!-- Nested lists -->
<ul id="nested-demo">
<li>Item 1
<ul>
<li>Subitem 1.1</li>
<li>Subitem 1.2</li>
</ul>
</li>
<li>Item 2</li>
</ul>
Configuration Options
Sortable.js offers numerous configuration options to customize its behavior:
Option | Type | Default | Description |
---|---|---|---|
group |
String or Object | null |
Group name or configuration for shared lists. Enables drag between lists with the same group name. |
sort |
Boolean | true |
Enables sorting within list. |
delay |
Number | 0 |
Time in milliseconds to define when the sorting should start. |
delayOnTouchOnly |
Boolean | false |
Only delay if user is using touch. |
animation |
Number | 150 |
Animation speed in milliseconds. |
handle |
String | null |
CSS selector for the drag handle element within list items. |
filter |
String | null |
CSS selector for elements that should not be draggable. |
draggable |
String | >* |
CSS selector for draggable items within the container. |
ghostClass |
String | sortable-ghost |
Class name for the drop placeholder. |
chosenClass |
String | sortable-chosen |
Class name for the chosen item. |
dragClass |
String | sortable-drag |
Class name for the dragging item. |
swapThreshold |
Number | 1 |
Threshold of the swap zone (0 to 1). |
invertSwap |
Boolean | false |
Inverts the swap zone if set to true. |
direction |
String | null |
Direction of sorting ('vertical', 'horizontal'). |
group
option with { name: 'shared', pull: true, put: true }
to enable drag-and-drop between multiple lists.
Events
Sortable.js provides a rich set of events to hook into the sorting lifecycle:
new Sortable(element, {
// Event callbacks
onStart: function(evt) {
// Element dragging started
console.log('Drag started', evt.oldIndex);
},
onEnd: function(evt) {
// Element dragging ended
console.log('Drag ended', evt.oldIndex, evt.newIndex);
},
onAdd: function(evt) {
// Element is dropped into the list from another list
console.log('Item added', evt.item);
},
onUpdate: function(evt) {
// Changed position within list
console.log('Item position changed', evt.oldIndex, evt.newIndex);
},
onSort: function(evt) {
// Called during sorting
console.log('Sorting in progress');
},
onRemove: function(evt) {
// Element is removed from the list into another list
console.log('Item removed', evt.item);
},
onChange: function(evt) {
// Element changed list or position
console.log('Change occurred', evt);
}
});
item
: The dragged elementfrom
: Source listto
: Destination listoldIndex
: Element's old index within the source listnewIndex
: Element's new index within the destination listoldDraggableIndex
: Element's old index within the source list, only counting draggable elementsnewDraggableIndex
: Element's new index within the destination list, only counting draggable elements
Methods
Sortable.js provides several methods for programmatic control:
// Create a Sortable instance
const sortable = new Sortable(document.getElementById('list'), {
// options
});
// Get the order of items as an array of IDs
const order = sortable.toArray();
// Sort elements in a predefined order
sortable.sort(['item-3', 'item-1', 'item-2']);
// Destroy the Sortable instance
sortable.destroy();
// Save the current order
const savedOrder = sortable.save();
// Option getter/setter
const currentAnimation = sortable.option('animation'); // getter
sortable.option('animation', 300); // setter
toArray()
method to get the current order of items, which is useful for saving the state to a database.
Plugins
Sortable.js includes several built-in plugins that extend its functionality:
Plugin | Description |
---|---|
AutoScroll |
Automatically scrolls the window when dragging near the edges. |
MultiDrag |
Enables selecting and dragging multiple items simultaneously. |
Swap |
Allows direct swapping of items instead of sorting. |
Revert |
Reverts items to their original position if dropped outside valid targets. |
Remove |
Removes items when dropped outside valid targets. |
To use a plugin, you need to activate it and configure it in your Sortable initialization:
// Enable MultiDrag plugin
Sortable.mount(new MultiDrag());
// Initialize with plugin options
new Sortable(element, {
multiDrag: true,
selectedClass: 'selected',
multiDragKey: 'CTRL'
});
Usage Examples
Basic Sortable List
// Simple sortable list
new Sortable(document.getElementById('simple-list'), {
animation: 150,
ghostClass: 'blue-background-class'
});
Shared Lists with Drag Between
// List A
new Sortable(document.getElementById('list-a'), {
group: 'shared',
animation: 150
});
// List B
new Sortable(document.getElementById('list-b'), {
group: 'shared',
animation: 150
});
Kanban Board
// Initialize each column as a Sortable list
document.querySelectorAll('.kanban-column').forEach(column => {
new Sortable(column, {
group: 'kanban',
animation: 150,
ghostClass: 'blue-background-class',
onEnd: function(evt) {
// Update task status based on the column it was moved to
const taskId = evt.item.dataset.id;
const newStatus = evt.to.dataset.status;
updateTaskStatus(taskId, newStatus);
}
});
});
Nested Lists
// Initialize parent list
new Sortable(document.getElementById('nested-parent'), {
group: 'nested',
animation: 150
});
// Initialize all child lists
document.querySelectorAll('.nested-child').forEach(list => {
new Sortable(list, {
group: 'nested',
animation: 150
});
});
Using MultiDrag Plugin
// Mount the MultiDrag plugin
Sortable.mount(new MultiDrag());
// Initialize with MultiDrag options
new Sortable(document.getElementById('multi-list'), {
animation: 150,
multiDrag: true,
selectedClass: 'selected',
multiDragKey: 'CTRL'
});
store
option to automatically save and restore the sort order using localStorage or a server API.
Best Practices
- Performance: For large lists (100+ items), consider using the
animation: 0
option to improve performance. - Mobile Support: Set
delayOnTouchOnly: true
anddelay: 100
for better touch device experience. - Accessibility: Add appropriate ARIA attributes to sortable elements to improve accessibility.
- Feedback: Use the
chosenClass
andghostClass
options to provide visual feedback during drag operations. - Nested Lists: When working with nested lists, ensure each list has the same group name but unique IDs.
- Saving State: Use the
onEnd
event to save the new order to your database or localStorage. - Drag Handles: For complex items, use the
handle
option to specify a drag handle to prevent accidental dragging when interacting with other elements.
Browser Support
Sortable.js supports all modern browsers including:
- Chrome
- Firefox
- Safari
- Edge
- IE11 (with polyfills)
- Mobile browsers (iOS Safari, Chrome for Android)
Troubleshooting
- Items not draggable? Check that your selector matches the items and that they don't have
position: static
. - Can't drag between lists? Ensure both lists have the same
group
name and thatpull
andput
options are correctly set. - Dragging starts immediately on touch devices? Add a
delay
and setdelayOnTouchOnly: true
. - Ghost element looks wrong? Customize the appearance with
ghostClass
or adjust your CSS. - Events not firing? Check that your event handlers are properly bound and that the Sortable instance is correctly initialized.
- Nested sortable not working? Ensure each nested list has its own Sortable instance with the same group name.
- Performance issues with large lists? Reduce or disable animation, or implement virtual scrolling for very large datasets.