Lattice Grid Buy a licence

developer guide

Full-Width Rows in a JavaScript Data Grid

A full-width row spans every column, which is where a banner, a section break, an empty-state message or a summary belongs. You draw it, the grid still virtualises it, and column groups can be formed and renamed while the reader works.

Developer guideColumns and cell rendering › Full-Width Rows in a JavaScript Data Grid

Full-width rows

A row drawn as a single band across every column instead of being divided into them: a section banner, an explanatory note, an empty-group message, a “load more” affordance, anything that belongs between rows and is not itself divided by the columns.

A banner before each section

createGrid(element, {
  columns,
  rows,   // your data, with the banners in it
  fullWidth: {
    when: (row) => row.data.kind === 'section',
    render: ({ data }) => data.title,
  },
});

render returns a string for text or a node for content, or returns nothing and writes into params.element itself. An HTML string is deliberately not accepted. params carries { row, data, index, grid, element }.

The band holds still while the columns scroll under it, which is what a banner is for: text that scrolled sideways out of view with the columns would be a worse version of a cell. It is drawn over the pinned regions as well as the centre, so it genuinely spans every column.

A full-width row is still one of your data rows. It is counted by rows.count(), sorted, filtered and exported like any other; only its presentation changes. That is the difference between this and pinned rows, and it is the thing to get straight before choosing between them: full-width changes how a row looks, pinned changes whether a row is data at all.

So if your banners must not appear in an export or a row count, they should not be in the data. If they are section headings that belong with the records they head, and should sort, filter and export alongside them: this is the right tool.

One consequence worth stating plainly: sorting reorders banners along with everything else, because the predicate follows the row and not its position. Either do not offer sorting on such a grid, or sort on a key that keeps each section together.

A band is exposed as a row containing one cell with aria-colspan covering every column, so a screen reader reads it as one wide cell rather than as a row with missing ones. No second, empty row is rendered underneath it.

Forming banded headers at runtime

Banded headers can be declared in config (columnGroups) and now also formed, renamed, moved and dissolved at runtime through grid.columns, with a keyboard equivalent for every action. The model is the single source of truth: a band made by interaction is the same ColumnGroup tree config drives, and it round-trips through a saved view.

The API. groupColumns(ids, { title, groupId }) wraps columns in a new band or adds them to an existing one; pass id instead of groupId to create a new band with a caller-chosen, stable id you can address later (groupColumns(ids, { title: 'Traffic', id: 'g-traffic' })). ungroupColumn(id) takes a column out (dissolving a band it empties); renameGroup(id, title), dissolveGroup(id) and moveGroup(id, to) do the rest. Each emits columngroup:changed. A band's columns are always contiguous, and a nested band dissolves into its parent, not the root.

The keyboard (WCAG 2.1.1). From a focused header cell: Ctrl+Shift+/ groups the column with its neighbour on that side (joining an adjacent band, or forming a new one); Ctrl+Shift+ takes it out of its band; and Alt+Shift+/ moves the whole band as a unit. Every action is announced through the live region, and a refusal - "not in a band", "cannot be moved there" - is announced too, never silent.

Pinning and visibility. A band lives in one pin region and draws over its visible columns there: hiding a column shrinks the band's span without changing the band definition, and hiding the last visible column hides the band. A band is exposed to assistive technology as one role="columnheader" cell with an aria-colspan.