ReoGrid ReoGrid Web

Moving Ranges, Rows & Columns

Since v1.5.0 a selection can be dragged to a new place, as in Excel. Two distinct operations share the gesture:

GestureOperationSemantics
Drag the selection borderMove a cell blockOverwrites the destination
Drag an already-selected row/column headerReorder whole rows/columnsCut-and-insert — nothing is lost

Available in both editions — moving is basic editing, like auto-fill and cut/paste.

What travels with the cells

The unit of relocation carries everything stored per cell:

value / formula text, rich text, number format, style, cell type, border, comment, and the locked flag.

Note: formulas that reference the moved cells are not rewritten in v1 — the same behavior as the existing cut/paste. A reorder of rows/columns also does not carry conditional-format, validation, table, page-break, outline or filter ranges.

Dragging

  • Grab the selection’s border (a ±3px band, inside the selection) — the cursor becomes a move cursor and a preview rectangle follows the pointer. Release to drop.
  • The fill handle on the bottom-right corner wins over the border, so auto-fill is unaffected.
  • Pressing an already-selected row or column header arms a reorder that only starts after 4px of travel, so a plain click still re-selects the line.

Turn the whole interaction off without touching the API:

grid.worksheet.setRangeMoveEnabled(false)

Programmatic moves

const ws = grid.worksheet

// Relocate a block — its top-left lands on (row, column). Overwrites.
ws.range('A1:C3').moveTo(10, 0)

// Or on the worksheet, with an explicit rectangle
ws.moveRange({ topRow: 0, leftColumn: 0, bottomRow: 2, rightColumn: 2 }, 10, 0)

// Reorder whole lines (cut-and-insert)
ws.moveRows(2, 3, 8)      // 3 rows starting at index 2 → before index 8
ws.moveColumns(1, 1, 4)   // 1 column at index 1 → before index 4

Each returns true when the move happened. To ask first:

const check = ws.canMoveRange({ topRow: 0, leftColumn: 0, bottomRow: 2, rightColumn: 2 }, 10, 0)
if (!check.ok) console.warn(check.reason)

canMoveRows / canMoveColumns are the line equivalents.

When a move is refused

A move is blocked when it would

  • tear a merged cell (partially move one),
  • land out of bounds,
  • hit a protected cell on a locked sheet, or
  • run while a delay-load data source is attached.

Events

onBeforeRangeMove is cancellable — the listener receives a mutable event and setting cancel = true suppresses the default move, so a host can implement its own behavior. It mirrors .NET ReoGrid’s BeforeCopyOrMoveRangeEventArgs.IsCancelled; the listener’s return value is not consulted.

const off = grid.onBeforeRangeMove((event) => {
  if (event.kind === 'rows' && event.from.topRow === 0) {
    event.cancel = true            // pin the header row
  }
})

grid.onAfterRangeMove((info) => {
  console.log(`${info.kind} moved`, info.from, '→', info.to)
})
  • A blocked move still fires onBeforeRangeMove, with cancel already true and a blockedReason ('merge-source', 'merge-target', 'out-of-bounds', 'protected', 'data-source', 'no-op') so you can explain the refusal. Clearing the flag does not force the move through.
  • onAfterRangeMove fires only on a completed move.
  • Undo/redo emit neither event — they replay the move directly.

Both follow the active sheet, so they keep working after a sheet switch. Each returns an unsubscribe function.

Undo

Every move is a single undo entry. A block move snapshots the source and destination rectangles; a line reorder stores the inverse permutation, so no snapshot is needed.

  • Selection & Events — the selection follows the moved block.
  • Auto Fill — the fill handle, which takes priority over the move border.
  • Clipboard — cut/paste, the other way to relocate data.
  • Row & Column — insert/delete, which shifts data rather than reordering it.
Stay Updated

Be first to know — get updates as they ship

Get notified of new releases, features, and announcements.
No spam — just updates that matter.