CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sortablejs

JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices

Pending
Overview
Eval results
Files

events.mddocs/

Events

Rich event lifecycle with callbacks for drag start/end, list changes, and custom interactions.

Capabilities

Drag Lifecycle Events

Events that track the complete drag and drop lifecycle from selection to completion.

onChoose

Called when an element is selected for dragging.

/**
 * Called when element is chosen (selected for dragging)
 * @param evt - Event object with oldIndex property
 */
onChoose?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  onChoose: (evt) => {
    console.log('Item chosen at index:', evt.oldIndex);
    evt.item.classList.add('being-dragged');
  }
});

onUnchoose

Called when element selection is cancelled before dragging starts.

/**
 * Called when element selection is cancelled
 * @param evt - Event object with same properties as onEnd
 */
onUnchoose?: (evt: SortableEvent) => void;

onStart

Called when dragging starts (after any configured delay).

/**
 * Called when dragging actually starts
 * @param evt - Event object with oldIndex and item properties
 */
onStart?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  onStart: (evt) => {
    console.log('Drag started:', evt.item.textContent);
    document.body.classList.add('dragging-active');
  }
});

onEnd

Called when dragging ends (whether successful or cancelled).

/**
 * Called when dragging ends
 * @param evt - Event object with comprehensive drag information
 */
onEnd?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  onEnd: (evt) => {
    document.body.classList.remove('dragging-active');
    
    if (evt.oldIndex !== evt.newIndex) {
      console.log(`Item moved from ${evt.oldIndex} to ${evt.newIndex}`);
      // Save new order
      saveOrder(evt.from);
    }
  }
});

List Change Events

Events that fire when the list structure changes due to drag operations.

onAdd

Called when an element is added to the list from another list.

/**
 * Called when element is added from another list
 * @param evt - Event object with same properties as onEnd
 */
onAdd?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(targetList, {
  onAdd: (evt) => {
    console.log('Item added from another list:', evt.item.textContent);
    console.log('Added at index:', evt.newIndex);
    
    // Update data model
    addItemToList(evt.item.dataset.id, evt.newIndex);
    
    // Animate the new item
    evt.item.classList.add('newly-added');
    setTimeout(() => evt.item.classList.remove('newly-added'), 300);
  }
});

onUpdate

Called when element order changes within the same list.

/**
 * Called when list order changes within the same list
 * @param evt - Event object with same properties as onEnd
 */
onUpdate?: (evt: SortableEvent) => void;

onRemove

Called when an element is removed from the list to another list.

/**
 * Called when element is removed to another list
 * @param evt - Event object with same properties as onEnd
 */
onRemove?: (evt: SortableEvent) => void;

onSort

Called for any list change (add, update, or remove).

/**
 * Called for any change to the list (add/update/remove)
 * @param evt - Event object with same properties as onEnd
 */
onSort?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  onSort: (evt) => {
    // This fires for any list change
    console.log('List changed:', evt.type);
    
    // Save to localStorage
    const order = Array.from(evt.to.children)
      .map(item => item.dataset.id);
    localStorage.setItem('list-order', JSON.stringify(order));
  }
});

Interaction Events

Events for handling special interactions and custom drag behavior.

onFilter

Called when attempting to drag a filtered (non-draggable) element.

/**
 * Called when trying to drag a filtered element
 * @param evt - Event object with item property
 */
onFilter?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  filter: '.locked',
  onFilter: (evt) => {
    console.log('Cannot drag locked item:', evt.item.textContent);
    
    // Show user feedback
    evt.item.classList.add('shake-animation');
    setTimeout(() => evt.item.classList.remove('shake-animation'), 500);
  }
});

onMove

Called when an element moves during dragging, allows controlling insertion behavior.

/**
 * Called when element moves during drag, can control insertion
 * @param evt - Event object with movement data
 * @param originalEvent - Original DOM event
 * @returns Control insertion: false (cancel), -1 (before), 1 (after), true/void (default)
 */
onMove?: (evt: SortableEvent, originalEvent: Event) => boolean | number | void;

Usage Examples:

Sortable.create(el, {
  onMove: (evt, originalEvent) => {
    // Prevent dropping on certain elements
    if (evt.related.classList.contains('no-drop')) {
      return false; // Cancel the move
    }
    
    // Custom insertion logic
    if (evt.related.classList.contains('insert-before')) {
      return -1; // Force insert before
    }
    
    if (evt.related.classList.contains('insert-after')) {
      return 1; // Force insert after
    }
    
    // Allow default behavior
    return true;
  }
});

onClone

Called when an element is cloned (in clone mode).

/**
 * Called when element is cloned
 * @param evt - Event object with item (original) and clone properties
 */
onClone?: (evt: SortableEvent) => void;

onChange

Called when the dragging element changes position during drag.

/**
 * Called when dragging element changes position
 * @param evt - Event object with newIndex property
 */
onChange?: (evt: SortableEvent) => void;

Usage Examples:

Sortable.create(el, {
  onChange: (evt) => {
    console.log('Element moved to index:', evt.newIndex);
    
    // Real-time preview of changes
    updatePreview(evt.newIndex);
  }
});

Event Object Properties

interface SortableEvent {
  /** Target list element */
  to: HTMLElement;
  
  /** Source list element */
  from: HTMLElement;
  
  /** The dragged element */
  item: HTMLElement;
  
  /** Clone element (when using clone mode) */
  clone?: HTMLElement;
  
  /** Element's old index within parent */
  oldIndex: number;
  
  /** Element's new index within parent */
  newIndex: number;
  
  /** Element's old index within parent, only counting draggable elements */
  oldDraggableIndex: number;
  
  /** Element's new index within parent, only counting draggable elements */
  newDraggableIndex: number;
  
  /** Pull mode when item is in another sortable: "clone" if cloning, true if moving */
  pullMode?: string | boolean;
  
  /** Element on which have guided (onMove event only) */
  related?: HTMLElement;
  
  /** DOMRect of related element (onMove event only) */
  relatedRect?: DOMRect;
  
  /** DOMRect of dragged element (onMove event only) */
  draggedRect?: DOMRect;
  
  /** Whether Sortable will insert drag element after target by default (onMove event only) */
  willInsertAfter?: boolean;
}

Usage Patterns

Complete Event Lifecycle

Sortable.create(el, {
  onChoose: (evt) => console.log('1. Item chosen'),
  onStart: (evt) => console.log('2. Drag started'),
  onChange: (evt) => console.log('3. Position changed'),
  onEnd: (evt) => console.log('4. Drag ended'),
  onAdd: (evt) => console.log('5a. Item added (if from another list)'),
  onUpdate: (evt) => console.log('5b. Item updated (if same list)'),
  onRemove: (evt) => console.log('5c. Item removed (if to another list)'),
  onSort: (evt) => console.log('6. Any sort change occurred')
});

Data Persistence

Sortable.create(el, {
  onEnd: (evt) => {
    // Save to server
    const newOrder = Array.from(evt.to.children)
      .map(item => item.dataset.id);
    
    fetch('/api/save-order', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ order: newOrder })
    });
  }
});

Cross-List Communication

const lists = [el1, el2, el3].map(el => 
  Sortable.create(el, {
    group: 'shared',
    onAdd: (evt) => {
      console.log(`Item moved to list ${evt.to.id}`);
      updateListCounters();
    },
    onRemove: (evt) => {
      console.log(`Item left list ${evt.from.id}`);
      updateListCounters();
    }
  })
);

Install with Tessl CLI

npx tessl i tessl/npm-sortablejs

docs

configuration.md

core-api.md

events.md

index.md

plugins.md

utilities.md

tile.json