Skip to content

Behaviors — Drag & Drop

A [and-drop-zone-sortable] zone auto-reorders its [and-draggable] children on drop. Drag an item to a new position:

🍎 Apple
🍌 Banana
🍒 Cherry
🍇 Grape
<div and-drop-zone and-drop-zone-sortable>
<div and-draggable>🍎 Apple</div>
<div and-draggable>🍌 Banana</div>
<div and-draggable>🍒 Cherry</div>
</div>

Dragging between two different sortable zones works the same way, purely from markup — see Recipes → Cross-list drag & drop.

AttributeWhereValuesNotes
and-draggabledraggableMarks an element as a native HTML5 drag source.
and-draggable-datadraggableany stringCarried through every event’s detail.data.
and-draggable-handledraggableCSS selectorOnly pointer-downs inside a matching descendant start the drag.
and-draggable-disableddraggableboolean attribute
and-draggable-typedraggableany string ('default')Matched against a drop zone’s and-drop-zone-accept list.
and-drop-zonedrop zoneMarks an element as a drop target.
and-drop-zone-acceptdrop zonecomma-separated typesOmit to accept every and-draggable-type.
and-drop-zone-sortabledrop zoneboolean attributeAuto-reorders (or moves between zones) on drop.
and-drop-zone-disableddrop zoneboolean attribute

bubbles: true, dispatched on the draggable or the zone:

EventDetail (AndDrag*Info)Dispatched on
and-dragstart{ data, element, event }draggable
and-dragend{ data, element, event, dropped }draggable
and-dragenter{ data, element, event, position } (position: 'before'|'after'|'inside')drop zone
and-dragoversame shape as and-dragenterdrop zone
and-dragleave— (no detail)drop zone
and-drop{ data, source, target, event, index? }index only set when sortable: truedrop zone
import {
createDraggable,
createDropZone,
} from '@andersseen/behaviors/drag-drop';
const item = createDraggable(itemEl, {
data: { id: 1 },
handle: '.drag-handle',
});
const zone = createDropZone(zoneEl, { accept: ['task'], sortable: true });
DragDropConfig (draggable)TypeNotes
dataunknownCarried through every event’s detail.data.
disabledbooleanSets draggable="false" and cursor: not-allowed.
handlestringCSS selector — only pointer-downs inside a matching descendant start the drag.
animationDurationnumberAccepted by the config type; not currently read by the drag logic.
DropZoneConfigTypeNotes
acceptstring[]Matched against the source’s and-draggable-type (default 'default'). Omit to accept everything.
disabledboolean
sortablebooleanOnly meaningful through defineBehaviors — the note above applies here too.

Both instances expose element, updateConfig(config), and destroy().

The drag preview shown while dragging is a rotated (3deg), 90%-opacity clone of the source element, positioned off-screen and set as the native dataTransfer drag image — there’s no config option to customize it today.

Low-level shared state, for driving custom drag sources that don’t start from a native dragstart (e.g. a touch-based custom implementation):

ExportSignature
startDrag(data, sourceElement, dragType?)(data: unknown, sourceElement: HTMLElement, dragType?: string) => void
endDrag()() => void
getDragState()() => Readonly<{ isDragging, data, sourceElement, dragType }>

The draggable element gets aria-grabbed ('true' while dragging), but native HTML5 drag-and-drop as used here has no built-in keyboard alternative — a keyboard-only or screen-reader user cannot reorder items through and-draggable/and-drop-zone alone. If reordering needs to be fully accessible, pair this with an explicit alternative (e.g. “Move up”/“Move down” buttons, or the Splitter’s pattern of real keyboard handlers) rather than relying on drag-and-drop as the only path.

Recipes → Cross-list drag & drop