Lightweight JS Sorting Library with Native HTML5 Drag and Drop – SortableJS

Category: Drag & Drop , Javascript , Recommended | January 14, 2024
Views Total:1,103 views
Official Page:Go to website
Last Update:January 14, 2024


Lightweight JS Sorting Library with Native HTML5 Drag and Drop – SortableJS


Sortable is a simple yet fully customizable javascript draggable library that makes a list of items sortable/reordered by using native Html5 drag and drop API. Works with all modern browsers and touch devices.

See Also:


$ npm install sortablejs --save

How to use it:

1. Import the SortableJS.

import Sortable from 'sortablejs';
// or
<script src="Sortable.min.js"></script>

2. Create a list of draggable items on the page.

<ul id="example">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>Item 5</li>
  <li>Item 6</li>
  <li>Item 7</li>
  <li>Item 8</li>

3. Enable the Draggable & Sortable functionality on the list.

new Sortable(document.getElementById('example'), {
    // options here

4. All possible options with default values.

new Sortable(el, {
    // name: String — group name
    // pull: true|false|["foo", "bar"]|'clone'|function — ability to move from the list. clone — copy the item, rather than move. Or an array of group names which the elements may be put in. Defaults to true.
    // put: true|false|["baz", "qux"]|function — whether elements can be added from other lists, or an array of group names from which elements can be added.
    // revertClone: boolean — revert cloned element to initial position after moving to a another list.
    group: "name",  // or { name: "...", pull: [true, false, 'clone', array], put: [true, false, array] }
    // enable sorting
    sort: true,  
    // time to wait before the sorting should start
    delay: 0
    // enable delay on touch
    delayOnTouchOnly: false,
    // how many pixels the point should move before cancelling a delayed drag event
    touchStartThreshold: 0, 
    // disables the sortable if set to true.
    disabled: false, 
    // which items inside the element should be draggable
    draggable: '>*'
    // save and restore the sort.
    store: null,
    // animation speed
    animation: 0,
    // easing function: "cubic-bezier(1, 0, 0, 1)"
    easing: null, 
    // drag handle
    handle: ".my-handle",
    // elements to ignore
    ignore: 'a, img',
    // filter selector
    filter: ".ignore-elements", 
    // preverntDefault when filtering
    preventOnFilter: true,
    // drop placeholder
    ghostClass: "sortable-ghost",
    // chosen class
    chosenClass: "sortable-chosen",
    // dragging class
    dragClass: "sortable-drag",
    // default data attribute
    dataIdAttr: 'data-id',
    // enable drop bubble
    dropBubble: false,
    // threshold of the swap zone
    swapThreshold: 1,
    // invert swap
    invertSwap: false,
    // threshold of the inverted swap zone
    invertedSwapThreshold: 1,
    // will be detected automatically if not given
    direction: 'horizontal',
    // ignore the HTML5 DnD behaviour
    forceFallback: false,
    // fallback class
    fallbackClass: "sortable-fallback",
    // appends the cloned DOM Element into the document body
    fallbackOnBody: false,  
    // how far the mouse should move before it's considered as a drag.
    fallbackTolerance: 0, 
    // fallback offsets
    fallbackOffset: {
        x: 0,
        y: 0
    dragoverBubble: false,
    // remove the cloned element when it is not showing
    removeCloneOnHide: true, 
    // distance mouse must be from empty sortable to insert drag element into it
    emptyInsertThreshold: 5, // px, 
    // set data
    setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
      dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
    // scroll plugin options
    // Enable the plugin. Can be HTMLElement.
    scroll: true, 
    // if you have custom scrollbar scrollFn may be used for autoscrolling
    scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, 
    // px, how near the mouse must be to an edge to start scrolling.
    scrollSensitivity: 30, 
    // force auto scroll fallback
    forceAutoScrollFallback: false,
    // px, speed of the scrolling
    scrollSpeed: 10, 
    // apply autoscroll to all parent elements, allowing for easier movement
    bubbleScroll: true,
    // OnSpill Plugin options
    // Enable plugin
    revertOnSpill: true, 
    // Called when item is spilled
    onSpill: function(/**Event*/evt) {
      evt.item // The spilled item
    // MultiDrag Plugin options
    // Enable the plugin
    multiDrag: true, 
    // Class name for selected item
    selectedClass: "sortable-selected", 
    // Key that must be down for items to be selected
    multiDragKey: null, 
    // Called when an item is selected
    onSelect: function(/**Event*/evt) {
      evt.item // The selected item
    // Called when an item is deselected
    onDeselect: function(/**Event*/evt) {
      evt.item // The deselected item
    // Swap Plugin options
    // Enable swap mode
    swap: true, 
    // Class name for swap item (if swap mode is enabled)
    swapClass: "sortable-swap-highlight",

Callback functions.

new Sortable(el, {
    // Element is chosen
    onChoose: function (/**Event*/evt) {
      evt.oldIndex;  // element index within parent
    // Element is unchosen
    onUnchoose: function(/**Event*/evt) {
      // same properties as onEnd
    // Element dragging started
    onStart: function (/**Event*/evt) {
      evt.oldIndex;  // element index within parent
    // Element dragging ended
    onEnd: function (/**Event*/evt) {
      var itemEl = evt.item;  // dragged HTMLElement;    // target list
      evt.from;  // previous list
      evt.oldIndex;  // element's old index within old parent
      evt.newIndex;  // element's new index within new parent
      evt.clone // the clone element
      evt.pullMode;  // when item is in another sortable: `"clone"` if cloning, `true` if moving
    // Element is dropped into the list from another list
    onAdd: function (/**Event*/evt) {
      // same properties as onEnd
    // Changed sorting within list
    onUpdate: function (/**Event*/evt) {
      // same properties as onEnd
    // Called by any change to the list (add / update / remove)
    onSort: function (/**Event*/evt) {
      // same properties as onEnd
    // Element is removed from the list into another list
    onRemove: function (/**Event*/evt) {
      // same properties as onEnd
    // Attempt to drag a filtered element
    onFilter: function (/**Event*/evt) {
      var itemEl = evt.item;  // HTMLElement receiving the `mousedown|tapstart` event.
    // Event when you move an item in the list or between lists
    onMove: function (/**Event*/evt, /**Event*/originalEvent) {
      // Example:,output
      evt.dragged; // dragged HTMLElement
      evt.draggedRect; // DOMRect {left, top, right, bottom}
      evt.related; // HTMLElement on which have guided
      evt.relatedRect; // DOMRect
      evt.willInsertAfter; // Boolean that is true if Sortable will insert drag element after target by default
      originalEvent.clientY; // mouse position
      // return false; — for cancel
      // return -1; — insert before target
      // return 1; — insert after target
    // Called when creating a clone of element
    onClone: function (/**Event*/evt) {
      var origEl = evt.item;
      var cloneEl = evt.clone;
    // Called when dragging element changes position
    onChange: function(/**Event*/evt) {
      evt.newIndex // most likely why this event is used is to get the dragging element's current index
      // same properties as onEnd


v1.15.2 (01/04/2024)

  • Bugfix

v1.15.1 (12/01/2023)

  • Fix multi drag sort event not firing
  • Only call onDrop on destroy if dragged element inside parent element
  • Prevent drag item from jumping to end of list if last element has smaller width/height

v1.15.0 (03/21/2023)

  • Make sure dragged element is inserted after last dragged element
  • Added avoidImplicitDeselect option to MultiDrag
  • Remove ID from cloned element
  • Remove ignoring click on Chrome for Android when dragging (wasn’t necessary)

v1.14.0 (07/04/2021)

  • Check if ghost is first
  • Fix multidrag indicies
  • Fix reverting with nested sortables
  • Added forceAutoScrollFallback option

v1.13.0 (01/09/2021)

  • Fix clicking select tags
  • Fix “insertBefore error” issue on nested lists
  • Improve repaint function for compressor scripts
  • Throw error when mounting duplicate plugins
  • Fix IE11 error when dragging element
  • Ignore pointer events on Safari (Fixes issue with Safari 13+)
  • Add useAnimation option to sort()

v1.12.0 (08/17/2020)

  • Updated

v1.11.0 (08/17/2020)

  • Updated

v1.10.2 (12/26/2019)

  • Add MSCSSMatrix to matrixFn options
  • Disable delayed drag events during destroy
  • Remove transform on drop
  • Added type check for ‘dragStarted’ variable
  • Fix(utils): Chrome < 44 not support scrollingElement
  • Fixed multiplying ghost transform
  • Added Open Collective

v1.10.1 (10/01/2019)

  • Fix OnSpill destroy error
  • Fix errors if imported in NodeJS context

v1.10.0 stable (09/16/2019)

  • Fix delayOnTouchOnly for android
  • Fix plugins’ references to options
  • Improve OnSpill support with multiple sortables + fix drop events
  • Remove MultiDrag events on GLOBAL destroy
  • Fix docs for enabling plugins
  • Support for transforms on ghost element
  • Animation performance improvements
  • Fix onSpill plugins on mobile
  • Allow selecting with SHIFT key without multiDragKey being down

v1.10.0rc3 (06/27/2019)

  • Fixed IE compatibility
  • Fixed delay issue (again)
  • Better loop safety

v1.10.0rc2 (06/07/2019)

  • Better documentation of plugin usage
  • Fixed issues where Sortable dependency was not available in plugins

v1.10.0rc1 (06/06/2019)

  • Added plugin system
  • Added MultiDrag plugin
  • Added Swap plugin
  • Added default OnSpill plugins
  • Better detection of empty Sortable
  • Animate all items
  • Only update ghostClass when necessary
  • Scroll adjustment after swap for Chrome

v1.9.0 (04/21/2019)

  • Use real event properties when doing empty insert
  • Fixed _lastChild failing to ignore display:none
  • Stricter requirements for supportPointer
  • Better fix for unwanted IE event capturing
  • Optimize nearestEmptyInsertDetectEvent callback
  • Added oldDraggableIndex + newDraggableIndex
  • Detect for empty insert always if not inserted
  • Fix infinite loop in detecting shadow DOM
  • Improved repaint trigger
  • Fixed delay option on Android
  • Added delayOnTouchOnly option

v1.8.4 (03/11/2019)

  • Automatic direction detection for grids
  • Better cross-browser compatibility for delay option
  • Fixed text selection in fallback on MacOS Safari
  • Fixed auto-scrolling in MacOS Safari
  • Added compensation for Chrome’s adjustment of scroll position if swapped element is out of viewport
  • Added pullMode to event object
  • Improved detection of empty sortables
  • Fixed setting of ghostClass, as well as animating items only after clones are shown

You Might Be Interested In:

Leave a Reply