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.

Key Features:
  • 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
});
Pro Tip: For optimal performance, apply Sortable to the container element rather than individual items. The library will automatically handle the draggable behavior for all child elements.

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>
Note: Sortable works with any element type, not just lists. You can use divs, cards, or any other container structure.

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').
Pro Tip: Use the 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);
    }
});
Event Object Properties:
  • item: The dragged element
  • from: Source list
  • to: Destination list
  • oldIndex: Element's old index within the source list
  • newIndex: Element's new index within the destination list
  • oldDraggableIndex: Element's old index within the source list, only counting draggable elements
  • newDraggableIndex: 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
Pro Tip: Use the 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'
});
Note: Some plugins may require additional CSS to properly style the interaction states.

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'
});
Pro Tip: For complex applications, consider using the 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 and delay: 100 for better touch device experience.
  • Accessibility: Add appropriate ARIA attributes to sortable elements to improve accessibility.
  • Feedback: Use the chosenClass and ghostClass 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)
Note: For IE11 support, you may need to include additional polyfills for ES6 features.

Troubleshooting

Common Issues:
  • 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 that pull and put options are correctly set.
  • Dragging starts immediately on touch devices? Add a delay and set delayOnTouchOnly: 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.