search ESC

Drag & Drop

A standalone, dependency-free drag & drop library built on the native HTML5 drag-and-drop API. Shipped as its own bundle lp-dragdrop-*.js / lp-dragdrop-*.css, loadable without launchpad.js.

How to load (standalone)

Drop these two tags into any HTML page — no launchpad.js required.

1. Basic sortable

Drag any item to reorder. Uses the native HTML5 drag-and-drop API.

  • Drag me anywhere
  • I can be reordered
  • No handle required
  • Works on touch too
2. Drag handle

Restrict drag initiation to a specific child element.

  • Only the handle starts a drag
  • Clicks on the text are ignored
  • Good for rows with buttons
3. Horizontal list

Direction is auto-detected from flex-direction.

  • Apple
  • Banana
  • Cherry
  • Date
  • Elderberry
4. Grid sortable

Works with CSS grid and flex-wrap layouts.

  • 🎨
  • 🚀
  • 🎯
  • 🔥
  • 💎
  • 🌟
  • 🎉
5. Live reorder vs drop-only

Two modes: liveReorder: true moves items in the DOM as you drag; liveReorder: false (default) only commits on drop.

Live reorder

  • Live Item 1
  • Live Item 2
  • Live Item 3

Drop only (default)

  • Drop Item 1
  • Drop Item 2
  • Drop Item 3
6. Cross-container (shared group)

Drag between containers with the same group name.

List A
  • Task 1
  • Task 2
  • Task 3
List B
  • Task 4
  • Task 5
7. Kanban (asymmetric pull/put rules)

Three columns, all sharing group kanban.

Todo
  • Design mockup
  • Write spec
  • Gather feedback
Doing
  • Build API
  • Create UI
Done
  • Project kickoff
8. Filter — pinned items are not draggable

Items matching filter selector cannot be dragged.

  • 📌 Pinned — cannot drag
  • Draggable
  • Draggable
  • 📌 Pinned — cannot drag
  • Draggable
9. Disabled state

Toggle drag on/off via enable() / disable().

  • Item 1
  • Item 2
  • Item 3
10. Storage persistence (localStorage)

Reorder, then refresh the page — order is remembered.

  • Persisted item 1
  • Persisted item 2
  • Persisted item 3
  • Persisted item 4
11. Callbacks (onStart, onEnd, onReorder)

Wire up callbacks via the constructor options.

  • Callback 1
  • Callback 2
  • Callback 3
12. Custom DOM events

Listen via element.addEventListener("lp:dragdrop:start", ...). Events bubble, so you can also listen on any ancestor.

  • Event item 1
  • Event item 2
  • Event item 3
12. API methods
  • One
  • Two
  • Three
  • Four

    
13. Different element types

Works with any HTML element, not just <li>. Here are <div>s and a sortable table body.

Divs

Div 1
Div 2
Div 3

Table rows

#NameRole
1AliceAdmin
2BobEditor
3CarolViewer
Configuration reference
OptionTypeDefaultPurpose
itemsselectornullWhich descendants are draggable (falls back to direct children)
handleselectornullRestrict drag to a handle element inside each item
filterselector\|fnnullItems matching are NOT draggable
groupstring\|objectnullGroup name for cross-container drag ({name, pull, put})
liveReorderbooleanfalseMove DOM during drag instead of only on drop
acceptselector\|fnnullValidate drop targets
disabledbooleanfalseInitially disabled
storage"local"\|"session"nullPersistence backend
storageKeystringauto from el.idCustom storage key
dataIdAttrstring"data-id"Attribute used for order serialization
dragRotationbooleantrueTilt the drag ghost 2° for visual flair
eventPrefixstring"lp"Prefix for dispatched DOM CustomEvents (e.g. lp:dragdrop:start)
onStart, onMove, onReorder, onEnd, onAdd, onRemove, onFilterfunctionnoopLifecycle callbacks
Data attributes (for auto-init)

Any option can be set declaratively via data-lp-*. kebab-case converts to camelCase.

AttributeMaps to
data-lp-component="dragdrop"Enables auto-init on the element
data-lp-handle=".handle"handle: ".handle"
data-lp-filter=".pinned"filter: ".pinned"
data-lp-group="shared"group: "shared"
data-lp-live-reorder="true"liveReorder: true
data-lp-storage="local"storage: "local"
data-lp-storage-key="my-key"storageKey: "my-key"
data-lp-disabled="true"disabled: true
Events reference

All events are CustomEvents dispatched on the container element. They bubble — you can listen on any ancestor. Every event carries a detail object with the relevant items.

EventFires whenevent.detail
lp:dragdrop:start A drag begins (dragstart fired, ghost captured) { item, from }
lp:dragdrop:move Cursor moves over an item during drag (every dragover) { item, from, to }
lp:dragdrop:reorder An item has been dropped to a new position { item, from, to }
lp:dragdrop:add An item was added to this container from another (cross-group drop) { item, from, to }
lp:dragdrop:remove An item was removed from this container into another { item, from, to }
lp:dragdrop:end Drag ends, whether dropped or cancelled { item, from }
lp:dragdrop:filter A drag was blocked because the item matched the filter { item }

Change the prefix via the eventPrefix option (defaults to "lp"). Setting it to "app" produces app:dragdrop:start, etc.

CSS classes applied by the plugin
ClassApplied to
.lp-dragdrop-dragSource item while it is being dragged (dimmed)
.lp-dragdrop-overItem currently being dragged over
.lp-dragdrop-over-beforeDrop indicator: drop will land before this item
.lp-dragdrop-over-afterDrop indicator: drop will land after this item
.lp-dragdrop-over-emptyEmpty container accepting drop at end
.lp-dragdrop-rejectTarget refuses the drop (accept validation failed)
.lp-dragdrop-disabledWhole container with drag disabled