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.
npm install @pantograph/sortableA modern JavaScript library for reorderable drag-and-drop lists with touch support.
- 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
``bash`
npm install sortablejs --save
`html`
`javascript
import Sortable from 'sortablejs';
const el = document.getElementById('items');
const sortable = Sortable.create(el);
`
bash
npm install sortablejs --save
`$3
`html
`$3
`javascript
// 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
$3
`javascript
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
});
`$3
`javascript
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
`javascript
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
$3
`javascript
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();
`$3
`javascript
// 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
$3
- AutoScroll: Automatic scrolling during drag operations
- OnSpill: Handle items dropped outside valid targets
-
RemoveOnSpill: Remove spilled items
- RevertOnSpill: Revert spilled items to original position$3
- Swap: Swap items instead of reordering
- FlatTree: Hierarchical tree-like drag and drop
$3
`javascript
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
$3
`html
- Item 1
- Item 2
- Item 3
``javascript
Sortable.create(document.getElementById('basic-list'));
`$3
`html
- ⋮⋮ Item 1
- ⋮⋮ Item 2
- ⋮⋮ Item 3
``javascript
Sortable.create(document.getElementById('handle-list'), {
handle: '.handle'
});
`$3
`html
Item 1
Item 2
Item 3
Item 4
``javascript
Sortable.create(document.getElementById('list1'), {
group: 'shared'
});Sortable.create(document.getElementById('list2'), {
group: 'shared'
});
`$3
`html
- Item 1
- Item 2
- Item 3
``javascript
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
1. Use Drag Handles: Reduces accidental drags
2. Limit Animation Duration: Keep animations under 200ms
3. Use Efficient Selectors: Avoid complex CSS selectors
4. Disable When Not Needed: Use
disabled` optionPlease read our Contributing Guide before submitting issues or pull requests.
MIT License - see LICENSE file for details.