JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices
—
Comprehensive configuration system with 25+ options for customizing drag behavior, visual feedback, and interaction patterns.
Control cross-list dragging and sharing of items between multiple sortable lists.
/**
* Group configuration for cross-list dragging
*/
interface GroupOptions {
/** Group name for identifying related lists */
name: string;
/** Whether items can be pulled from this list */
pull?: boolean | string | string[] | ((to: Sortable, from: Sortable) => boolean);
/** Whether items can be put into this list */
put?: boolean | string | string[] | ((to: Sortable, from: Sortable) => boolean);
/** Whether to revert cloned items when dragging ends */
revertClone?: boolean;
}
/** Group option can be string (group name) or full configuration object */
group?: string | GroupOptions;Usage Examples:
// Simple group sharing
const list1 = Sortable.create(el1, { group: 'shared' });
const list2 = Sortable.create(el2, { group: 'shared' });
// Advanced group configuration
const sourceList = Sortable.create(sourceEl, {
group: {
name: 'shared',
pull: 'clone', // Clone items when pulling
put: false // Don't accept items from other lists
}
});
const targetList = Sortable.create(targetEl, {
group: {
name: 'shared',
pull: false, // Don't allow pulling from this list
put: true // Accept items from other lists
}
});Core options that control the fundamental sortable behavior.
/** Enable/disable sorting within the list */
sort?: boolean; // default: true
/** Disable all drag operations */
disabled?: boolean; // default: false
/** Custom storage adapter for save/restore functionality */
store?: {
get: (sortable: Sortable) => string[];
set: (sortable: Sortable) => void;
} | null; // default: nullDefine which elements can be dragged and how to identify them.
/** CSS selector for drag handle elements within items */
handle?: string | null; // default: null
/** CSS selector for draggable items */
draggable?: string; // default: ">*" (">li" for ul/ol elements)
/** CSS selector for elements that should be ignored during drag */
ignore?: string; // default: "a, img"
/** Filter function or selector for non-draggable elements */
filter?: string | ((evt: Event, item: HTMLElement, sortable: Sortable) => boolean) | null; // default: null
/** Call preventDefault when filter matches */
preventOnFilter?: boolean; // default: trueUsage Examples:
// Only allow dragging by handle
Sortable.create(el, {
handle: '.drag-handle',
draggable: '.sortable-item'
});
// Filter out certain elements
Sortable.create(el, {
filter: '.no-drag',
onFilter: (evt) => {
console.log('Attempted to drag filtered element');
}
});
// Custom filter function
Sortable.create(el, {
filter: (evt, item, sortable) => {
return item.classList.contains('locked');
}
});Control the visual appearance during drag operations.
/** CSS class for the ghost placeholder element */
ghostClass?: string; // default: "sortable-ghost"
/** CSS class for the chosen/active element */
chosenClass?: string; // default: "sortable-chosen"
/** CSS class for the element being dragged */
dragClass?: string; // default: "sortable-drag"Configure animations and visual transitions.
/** Animation duration in milliseconds (0 = no animation) */
animation?: number; // default: 0
/** CSS easing function for animations */
easing?: string | null; // default: nullUsage Examples:
// Smooth animations with custom easing
Sortable.create(el, {
animation: 300,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
ghostClass: 'ghost-style',
chosenClass: 'chosen-style'
});Fine-tune when and how elements swap positions during dragging.
/** Threshold percentage (0-1) for triggering element swaps */
swapThreshold?: number; // default: 1
/** Always use inverted swap zone */
invertSwap?: boolean; // default: false
/** Threshold for inverted swap zone */
invertedSwapThreshold?: number | null; // default: null (uses swapThreshold value)
/** Sort direction detection */
direction?: 'horizontal' | 'vertical' | ((evt: Event, target: HTMLElement, dragEl: HTMLElement) => string); // default: auto-detect functionConfigure how data is handled during drag operations.
/** HTML attribute used by toArray() method for item identification */
dataIdAttr?: string; // default: "data-id"
/** Function to set drag data on dataTransfer object */
setData?: (dataTransfer: DataTransfer, dragEl: HTMLElement) => void; // default: sets text contentControl when and how drag operations start.
/** Delay in milliseconds before drag starts */
delay?: number; // default: 0
/** Apply delay only on touch devices */
delayOnTouchOnly?: boolean; // default: false
/** Pixels to move before canceling delayed drag on touch devices */
touchStartThreshold?: number; // default: 1 (or devicePixelRatio)Usage Examples:
// Delay drag start to prevent accidental drags
Sortable.create(el, {
delay: 100,
delayOnTouchOnly: true, // Only delay on touch devices
touchStartThreshold: 5 // Allow 5px movement before canceling
});Configure behavior when HTML5 drag and drop is not available or forced off.
/** Force fallback mode (ignore HTML5 drag and drop) */
forceFallback?: boolean; // default: false
/** CSS class for the fallback drag element */
fallbackClass?: string; // default: "sortable-fallback"
/** Append fallback element to document body */
fallbackOnBody?: boolean; // default: false
/** Mouse movement tolerance before drag starts in fallback mode */
fallbackTolerance?: number; // default: 0
/** Offset for fallback element positioning */
fallbackOffset?: { x: number; y: number }; // default: {x: 0, y: 0}Control event bubbling and interaction patterns.
/** Allow drop events to bubble up */
dropBubble?: boolean; // default: false
/** Allow dragover events to bubble up */
dragoverBubble?: boolean; // default: false
/** Use pointer events if available */
supportPointer?: boolean; // default: auto-detectedSpecialized options for advanced use cases.
/** Remove clone element when hidden rather than just hiding it */
removeCloneOnHide?: boolean; // default: true
/** Distance in pixels from empty sortable to insert drag element */
emptyInsertThreshold?: number; // default: 5Usage Examples:
// Configure clone behavior and empty insertion
Sortable.create(el, {
removeCloneOnHide: false, // Hide clone instead of removing from DOM
emptyInsertThreshold: 10 // Increase sensitivity for empty lists
});
// Useful for custom styling during drag
Sortable.create(el, {
removeCloneOnHide: false,
onClone: (evt) => {
// Style the clone since it remains in DOM when hidden
evt.clone.style.visibility = 'hidden';
evt.clone.style.opacity = '0.3';
}
});interface SortableOptions {
// Group and sharing
group?: string | GroupOptions;
// Basic behavior
sort?: boolean;
disabled?: boolean;
store?: { get: (sortable: Sortable) => string[]; set: (sortable: Sortable) => void; } | null;
// Element selection
handle?: string | null;
draggable?: string;
ignore?: string;
filter?: string | ((evt: Event, item: HTMLElement, sortable: Sortable) => boolean) | null;
preventOnFilter?: boolean;
// Visual feedback
ghostClass?: string;
chosenClass?: string;
dragClass?: string;
// Animation
animation?: number;
easing?: string | null;
// Swap detection
swapThreshold?: number;
invertSwap?: boolean;
invertedSwapThreshold?: number | null;
direction?: 'horizontal' | 'vertical' | ((evt: Event, target: HTMLElement, dragEl: HTMLElement) => string);
// Data transfer
dataIdAttr?: string;
setData?: (dataTransfer: DataTransfer, dragEl: HTMLElement) => void;
// Interaction timing
delay?: number;
delayOnTouchOnly?: boolean;
touchStartThreshold?: number;
// Fallback mode
forceFallback?: boolean;
fallbackClass?: string;
fallbackOnBody?: boolean;
fallbackTolerance?: number;
fallbackOffset?: { x: number; y: number };
// Events
dropBubble?: boolean;
dragoverBubble?: boolean;
supportPointer?: boolean;
// Advanced
removeCloneOnHide?: boolean;
emptyInsertThreshold?: number;
// Event callbacks (see events.md for details)
onChoose?: (evt: SortableEvent) => void;
onUnchoose?: (evt: SortableEvent) => void;
onStart?: (evt: SortableEvent) => void;
onEnd?: (evt: SortableEvent) => void;
onAdd?: (evt: SortableEvent) => void;
onUpdate?: (evt: SortableEvent) => void;
onSort?: (evt: SortableEvent) => void;
onRemove?: (evt: SortableEvent) => void;
onFilter?: (evt: SortableEvent) => void;
onMove?: (evt: SortableEvent, originalEvent: Event) => boolean | number | void;
onClone?: (evt: SortableEvent) => void;
onChange?: (evt: SortableEvent) => void;
}Install with Tessl CLI
npx tessl i tessl/npm-sortablejs