@pantograph/sortable
v2.2.3
Published
JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap.
Maintainers
Readme
Sortable.js
A modern JavaScript library for reorderable drag-and-drop lists with touch support.
Features
- Touch Support: Works on mobile devices and touch screens
- Modern Browsers: Full support for all modern browsers (including IE9+)
- Cross-List Dragging: Drag items between different lists
- Smooth Animations: CSS animations and transitions
- Drag Handles: Support for drag handles and selectable text
- Auto-Scrolling: Smart auto-scroll during drag operations
- Plugin System: Extensible with plugins
- Framework Support: Works with React, Vue, Angular, and more
- No Dependencies: Pure JavaScript, no jQuery required
- TypeScript: Full TypeScript definitions available
Quick Start
Installation
npm install sortablejs --saveBasic Usage
<ul id="items">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>import Sortable from 'sortablejs';
const el = document.getElementById('items');
const sortable = Sortable.create(el);Installation Options
NPM
npm install sortablejs --saveCDN
<script src="sortable.min.js"></script>Import Options
// Default SortableJS (with default plugins)
import Sortable from 'sortablejs';
// Core SortableJS (without plugins)
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
// Complete SortableJS (with all plugins)
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
// Cherry-pick plugins
import Sortable, { Swap, AutoScroll } from 'sortablejs';Configuration
Basic Options
const sortable = Sortable.create(el, {
group: "name", // Group name for cross-list dragging
sort: true, // Enable sorting within list
delay: 0, // Delay before drag starts (ms)
disabled: false, // Disable the sortable
animation: 150, // Animation duration (ms)
handle: ".drag-handle", // Drag handle selector
filter: ".ignore", // Elements to ignore
draggable: ".item", // Draggable elements
ghostClass: "sortable-ghost", // Class for drop placeholder
chosenClass: "sortable-chosen", // Class for chosen item
dragClass: "sortable-drag", // Class for dragging item
swapThreshold: 0.65, // Swap zone threshold
direction: 'vertical', // Sort direction
forceFallback: false, // Force fallback mode
});Advanced Options
const sortable = Sortable.create(el, {
// Group configuration
group: {
name: "shared",
pull: true, // Can pull from this group
put: true, // Can put into this group
revertClone: true // Revert cloned element
},
// Animation settings
animation: 150,
easing: "cubic-bezier(1, 0, 0, 1)",
// Touch settings
delayOnTouchOnly: false,
touchStartThreshold: 3,
// Fallback settings
fallbackClass: "sortable-fallback",
fallbackOnBody: false,
fallbackTolerance: 0,
// Scroll settings
scroll: true,
scrollSensitivity: 30,
scrollSpeed: 10,
// Event handlers
onStart: function(evt) {
console.log('Drag started');
},
onEnd: function(evt) {
console.log('Drag ended');
}
});Event Handlers
const sortable = Sortable.create(el, {
// Element is chosen
onChoose: function(evt) {
console.log('Element chosen:', evt.item);
},
// Element is unchosen
onUnchoose: function(evt) {
console.log('Element unchosen:', evt.item);
},
// Element dragging started
onStart: function(evt) {
console.log('Drag started:', evt.item);
console.log('Old index:', evt.oldIndex);
},
// Element dragging ended
onEnd: function(evt) {
console.log('Drag ended');
console.log('Item:', evt.item);
console.log('From:', evt.from);
console.log('To:', evt.to);
console.log('Old index:', evt.oldIndex);
console.log('New index:', evt.newIndex);
},
// Element is added to list
onAdd: function(evt) {
console.log('Element added:', evt.item);
},
// Element is removed from list
onRemove: function(evt) {
console.log('Element removed:', evt.item);
},
// List order changed
onUpdate: function(evt) {
console.log('List updated');
},
// Any change to list
onSort: function(evt) {
console.log('List sorted');
},
// Element is filtered
onFilter: function(evt) {
console.log('Element filtered:', evt.item);
},
// Element is moved
onMove: function(evt, originalEvent) {
console.log('Element moved');
// Return false to cancel
// Return -1 to insert before
// Return 1 to insert after
return true;
},
// Clone is created
onClone: function(evt) {
console.log('Clone created:', evt.clone);
},
// Element position changed
onChange: function(evt) {
console.log('Element changed position');
}
});Methods
Instance Methods
const sortable = Sortable.create(el);
// Get/set options
sortable.option('disabled', true);
const disabled = sortable.option('disabled');
// Get array of item IDs
const order = sortable.toArray();
// Sort items by array
sortable.sort(['item3', 'item1', 'item2']);
// Save current order
sortable.save();
// Destroy sortable
sortable.destroy();Static Methods
// Create new instance
const sortable = Sortable.create(el, options);
// Get instance from element
const sortable = Sortable.get(element);
// Mount plugin
Sortable.mount(Plugin);
// Get active instance
const active = Sortable.active;
// Get dragged element
const dragged = Sortable.dragged;Plugins
Default Plugins (Included)
- AutoScroll: Automatic scrolling during drag operations
- OnSpill: Handle items dropped outside valid targets
RemoveOnSpill: Remove spilled itemsRevertOnSpill: Revert spilled items to original position
Extra Plugins
- Swap: Swap items instead of reordering
- FlatTree: Hierarchical tree-like drag and drop
Plugin Usage
import Sortable, { Swap, AutoScroll } from 'sortablejs';
// Mount plugins
Sortable.mount(Swap);
Sortable.mount(AutoScroll);
// Use plugins
const sortable = Sortable.create(el, {
swap: true,
scroll: true
});Examples
Basic List
<ul id="basic-list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>Sortable.create(document.getElementById('basic-list'));Drag Handles
<ul id="handle-list">
<li><span class="handle">⋮⋮</span> Item 1</li>
<li><span class="handle">⋮⋮</span> Item 2</li>
<li><span class="handle">⋮⋮</span> Item 3</li>
</ul>Sortable.create(document.getElementById('handle-list'), {
handle: '.handle'
});Cross-List Dragging
<div id="list1" class="list">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
</div>
<div id="list2" class="list">
<div class="item">Item 3</div>
<div class="item">Item 4</div>
</div>Sortable.create(document.getElementById('list1'), {
group: 'shared'
});
Sortable.create(document.getElementById('list2'), {
group: 'shared'
});Bootstrap Integration
<ul class="list-group" id="bootstrap-list">
<li class="list-group-item">Item 1</li>
<li class="list-group-item">Item 2</li>
<li class="list-group-item">Item 3</li>
</ul>Sortable.create(document.getElementById('bootstrap-list'));Browser Support
- Modern Browsers: Chrome, Firefox, Safari, Edge
- Mobile: iOS Safari, Chrome Mobile, Firefox Mobile
- Legacy: Internet Explorer 9+
- Touch Devices: Full touch support
Performance Tips
- Use Drag Handles: Reduces accidental drags
- Limit Animation Duration: Keep animations under 200ms
- Use Efficient Selectors: Avoid complex CSS selectors
- Disable When Not Needed: Use
disabledoption - Optimize Event Handlers: Keep handlers lightweight
Contributing
Please read our Contributing Guide before submitting issues or pull requests.
License
MIT License - see LICENSE file for details.
