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
-
Pointer API Concurrency: Unified mouse and touch track tracking. The component locks screen scrolling on drag handles using
touch-action: noneto prevent drag delays or stuttering on mobile screens. -
Attribute Governance: Drag status is managed entirely by the
data-ln-sortableattribute on the parent list. Setting it to"disabled"deactivates sorting instantly, which is perfect during network saves or locked layouts. -
Clean Telemetry Hooks: Emits clean cancelable events (
ln-sortable:before-drag) to prevent accidental drags, and completion notifications (ln-sortable:reordered) detailing the exactoldIndexandnewIndexshifts for instant store updates.
For the full guide see the ln-sortable README and the architecture reference.
Attributes
| Attribute | On element | Description |
|---|---|---|
data-ln-sortable | container | Creates an instance. Sortable items = direct children. |
data-ln-sortable-handle | element inside a child | Drag handle. If absent — the entire child is draggable. |
Simple List
No handle — drag anywhere on the item.
- First item
- Second item
- Third item
- Fourth item
- Fifth item
List with Drag Handle
Drag only by the handle.
- Dashboard
- Users
- Reports
- Settings
- Archive
Enable / Disable
- Alpha
- Beta
- Gamma
Events Log
- Item One
- Item Two
- Item Three
- Item Four
CSS Classes
The component adds/removes these classes. Does not ship CSS — the consumer styles them.
| Class | On | When |
|---|---|---|
.ln-sortable--active | container | While drag is in progress |
.ln-sortable--dragging | dragged element | While being dragged |
.ln-sortable--drop-before | target element | Pointer in top half |
.ln-sortable--drop-after | target element | Pointer in bottom half |
Events
| Event | Bubbles | Cancelable | detail |
|---|---|---|---|
ln-sortable:before-drag | yes | yes | { item, index } |
ln-sortable:drag-start | yes | no | { item, index } |
ln-sortable:reordered | yes | no | { item, oldIndex, newIndex } |
ln-sortable:enabled | yes | no | { target } |
ln-sortable:disabled | yes | no | { 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);