Sortable

Mode

Skin

Theme

Overview & Philosophy

ln-sortable is applied to list containers via <ul data-ln-sortable> or <ol data-ln-sortable>. Built on high-performance native Pointer Events, it listens for pointer interactions on list items or dedicated drag handles ([data-ln-sortable-handle]). During drag operations, it assigns .ln-sortable--dragging, inserts an interactive placeholder, and dynamically reorders items in the DOM via insertBefore. Upon release, it cleans up placeholders and dispatches an ln-sortable:change CustomEvent with the reordered DOM sequence.

In ln-ashlar, this zero-dependency primitive handles drag-and-drop reordering natively across mouse and touch devices without heavy third-party libraries.

The Reordering Doctrine

A sortable primitive is not responsible for updating databases, syncing state stores, or managing layout transitions. It acts as an isolated DOM manipulator. It provides layout shifts and dispatches index changes, leaving styling to standard CSS classes. The HTML container attribute is the sole contract.

Architectural Design Features

  1. Pointer API Concurrency: Unified mouse and touch track tracking. The component locks screen scrolling on drag handles using touch-action: none to prevent drag delays or stuttering on mobile screens.
  2. Attribute Governance: Drag status is managed entirely by the data-ln-sortable attribute on the parent list. Setting it to "disabled" deactivates sorting instantly, which is perfect during network saves or locked layouts.
  3. Clean Telemetry Hooks: Emits clean cancelable events (ln-sortable:before-drag) to prevent accidental drags, and completion notifications (ln-sortable:reordered) detailing the exact oldIndex and newIndex shifts for instant store updates.

For the full guide see the ln-sortable README and the architecture reference.

Attributes

AttributeOn elementDescription
data-ln-sortablecontainerCreates an instance. Sortable items = direct children.
data-ln-sortable-handleelement inside a childDrag handle. If absent — the entire child is draggable.

Simple List

No handle — drag anywhere on the item.

  1. First item
  2. Second item
  3. Third item
  4. Fourth item
  5. Fifth item

List with Drag Handle

Drag only by the handle.

  1. Dashboard
  2. Users
  3. Reports
  4. Settings
  5. Archive

Enable / Disable

  1. Alpha
  2. Beta
  3. Gamma

Events Log

  1. Item One
  2. Item Two
  3. Item Three
  4. Item Four

    CSS Classes

    The component adds/removes these classes. Does not ship CSS — the consumer styles them.

    ClassOnWhen
    .ln-sortable--activecontainerWhile drag is in progress
    .ln-sortable--draggingdragged elementWhile being dragged
    .ln-sortable--drop-beforetarget elementPointer in top half
    .ln-sortable--drop-aftertarget elementPointer in bottom half

    Events

    EventBubblesCancelabledetail
    ln-sortable:before-dragyesyes{ item, index }
    ln-sortable:drag-startyesno{ item, index }
    ln-sortable:reorderedyesno{ item, oldIndex, newIndex }
    ln-sortable:enabledyesno{ target }
    ln-sortable:disabledyesno{ target }
    document.addEventListener('ln-sortable:reordered', function (e) {
        console.log('Moved from', e.detail.oldIndex, 'to', e.detail.newIndex);
    });
    
    // Cancel drag conditionally
    document.addEventListener('ln-sortable:before-drag', function (e) {
        if (listIsLocked) e.preventDefault();
    });
    
    // Disable via attribute (single source of truth)
    list.setAttribute('data-ln-sortable', 'disabled');

    API

    var list = document.querySelector('[data-ln-sortable]');
    
    // State is written on the attribute; the observer syncs internals
    list.setAttribute('data-ln-sortable', 'disabled');   // disable
    list.setAttribute('data-ln-sortable', '');           // enable
    
    list.lnSortable.isEnabled;    // boolean (read-only query)
    
    // Constructor — only for non-standard cases
    window.lnSortable(container);