DOM
gridla/dom is the vanilla adapter. mountGrid(element, options) turns an element into a canvas: it creates and positions one element per item, wires pointer and keyboard input, measures the element with ResizeObserver, and reports every committed layout. It is a thin binding over gridla/interaction, the same layer the React adapter is built on, so behavior is identical across adapters.
Appearance is yours: see the styling guide for every data-gridla-* attribute, resize handle sizing, the preview outline, and a starter stylesheet.
Install
The adapter ships inside the gridla package as the gridla/dom subpath. It has no dependencies.
Minimal example
What happens:
- The element receives
data-gridla-canvas,position: relative,touch-action: none, andtabindex="0"(unless it already has one) so it can take keyboard focus. - Each item becomes a
divwithdata-gridla-item="<id>", positioned withtransform(orleft/topwithpositioning: 'absolute'). The whole element is a drag surface unlessdraggable: false, in which case only descendants you mark withdata-gridla-drag-handlestart a move. resizeEdgesadds built-in resize handles (data-gridla-resize-handle+data-gridla-edge). They are children of the item element and are re-attached after everyrenderItemcall, so replacinginnerHTMLis safe.preview: truerenders adata-gridla-previewelement where the active item will land; pass your own element instead oftrueto use it.renderItem(item, element, view)is called when an item's element is created and whenever the item or itsGridItemView(rect, active, selected, shifted, transferring) changes. Without a renderer each element shows the item id.
The item, canvas, and preview elements carry the same data-gridla-* attributes as every adapter, so one stylesheet works everywhere. See the React quickstart for a minimal one.
Controlled and uncontrolled
With defaultLayout the handle owns the layout. getLayout() returns the current one and onLayoutChange still fires after every accepted change.
With layout you own it: the canvas keeps showing what you passed until you call setLayout with the next one. This is the shape to use when the layout lives in your own store.
The second argument is a GridChangeDetail with the reason (move, resize, place, remove, update, transfer, set), the itemId, and the solver strategy. onCommit fires with the same detail after every interactive commit only.
Options can change after mounting with grid.setOptions({ gap: 16 }); the canvas re-renders once. The transfer scope is fixed at mount time.
Nested layouts and transfers
A nested layout is a second mountGrid inside an item's element. Give both mounts the same scope from createTransferScope and items can be dragged between them; the pointer decides the target, the target previews the drop, and releasing commits it.
The item element moves with the item: after a transfer the source mount removes its element and the target mount creates one, calling its own renderItem.
Handle
mountGrid returns a GridHandle:
controller.actions exposes move, resize, place, remove, update, select, setLayout, and cancel; every one goes through the same solvers as a gesture.
Server rendering
Importing gridla/dom touches no window or document. Call mountGrid after the element exists in a browser (for example in a DOMContentLoaded handler or a framework's mount hook). To avoid a flash of unpositioned content, render the server markup with the item geometry already applied, then mount over it; the adapter reuses nothing from the server markup, so keep the server-rendered items out of the element you mount on (or clear it first).
API
mountGrid,MountGridOptions,GridHandleGridItemRenderer,GridItemView,selectItemViewcreateTransferScope,GRID_DATA- Types:
GridState,GridChangeDetail,GridPreview,GridInteraction
Try it: the DOM demo app drives the shared adapter e2e suite.