Extending Selection With Shift and Arrow Keys

Permalink to "Extending Selection With Shift and Arrow Keys"

Keyboard selection in a grid works like text selection in an editor: an anchor stays where selection began, a focus moves with the arrow keys, and the selection is everything between them. Shift+Arrow moves the focus corner; plain arrows collapse the selection and move on. Without this model, keyboard users can select one cell at a time and nothing more, which makes copy, bulk edit and delete impossible to use without a mouse.

This page specifies the full key set and the anchor rules. It is part of cell selection & clipboard in data grids; the announcement for the resulting range is in announcing rectangular cell range selections.

Spec reference

Permalink to "Spec reference"

The ARIA Authoring Practices grid pattern lists these optional selection keys for grids that support selection:

  • Ctrl+Space — select the column containing focus.
  • Shift+Space — select the row containing focus.
  • Ctrl+A — select all cells.
  • Shift+Right/Left/Down/Up Arrow — extend the selection by one cell.

It adds that multi-selectable grids should set aria-multiselectable="true" and reflect state with aria-selected. Common spreadsheet extensions — Shift+Home/End, Shift+Page Up/Page Down, Ctrl+Shift+Arrow to the data edge — are not in the pattern but are widely expected.

WCAG: SC 2.1.1 Keyboard requires that anything possible with a pointer (drag-selecting a range) is possible with the keyboard. SC 2.1.4 Character Key Shortcuts does not apply here because every selection shortcut uses a modifier.

Anchor, focus and the rectangle between them Mock grid showing the anchor cell where Shift-selection began, the focus cell after three Shift+Down and one Shift+Right presses, and the selected rectangle. Anchor, focus and the rectangle between themTaskMonTueWedThuDesign42101Build2673Review0125Release100221Anchor — set on the first Shift+Arrow(Design, Tue)2Focus — moved by Shift+Down ×3 andShift+Right (Release, Wed)3Selection is the 2 × 4 rectangle betweenthem
Shift+Arrow moves only the focus corner; the anchor stays put until selection collapses.

When to support each key — and when not to

Permalink to "When to support each key — and when not to"

Support Shift+Arrow and Ctrl+A in any grid with multi-selection; they are the minimum for keyboard parity with drag-selection. Add Shift+Space and Ctrl+Space when rows and columns are meaningful selection units (they usually are in data grids).

Do not implement selection keys in a static table. Browse-mode screen readers own the arrow keys there, and the grid keyboard model does not apply. Use checkboxes, as described in aria-selected on cells versus rows.

The misapplication to name is moving the anchor on every Shift+Arrow. It produces a selection that is always exactly two cells — the previous focus and the current one — which looks broken to sighted users and is incomprehensible by ear.

Annotated code example

Permalink to "Annotated code example"
// Selection extension on a grid with roving focus
grid.addEventListener('keydown', (e) => {
  const pos = focusPos();                    // { row, col } of the focused cell
  const k = e.key;

  if (e.shiftKey && k.startsWith('Arrow')) {
    if (!sel.anchor) sel.anchor = { ...pos };             // set once
    sel.focus = step(pos, k, e.ctrlKey ? 'edge' : 1);     // Ctrl+Shift: to data edge
    moveFocus(sel.focus);
    paintSelection(); announceRange();                    // SC 2.1.1 + 4.1.3
    e.preventDefault(); return;
  }
  if (e.shiftKey && k === ' ') {                          // Shift+Space: row
    sel.anchor = { row: pos.row, col: firstCol() };
    sel.focus = { row: pos.row, col: lastCol() };
    paintSelection(); status(`${rowName(pos.row)} row selected.`);
    e.preventDefault(); return;
  }
  if (e.ctrlKey && k === ' ') {                           // Ctrl+Space: column
    sel.anchor = { row: firstRow(), col: pos.col };
    sel.focus = { row: lastRow(), col: pos.col };
    paintSelection(); status(`${colName(pos.col)} column selected.`);
    e.preventDefault(); return;
  }
  if ((e.ctrlKey || e.metaKey) && k.toLowerCase() === 'a') {  // Ctrl/Cmd+A: all
    sel.anchor = { row: firstRow(), col: firstCol() };
    sel.focus = { row: lastRow(), col: lastCol() };
    paintSelection(); status(`All ${cellCount()} cells selected.`);
    e.preventDefault(); return;                           // not the page text
  }
  if (k === 'Escape' && sel.anchor) {                     // collapse
    sel.anchor = null; sel.focus = null; paintSelection();
    status('Selection cleared.'); return;
  }
  if (k.startsWith('Arrow')) {                            // plain arrow: collapse + move
    sel.anchor = null; sel.focus = null; paintSelection();
    moveFocus(step(pos, k, 1));
  }
});

Note that Ctrl+Space is intercepted by some operating systems for input-method switching (notably in Chinese and Japanese Windows configurations). Offer a second route to column selection — activating the column header with Shift+Space, or a menu command — so users on those systems are not locked out.

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Key Action Expected announcement Collision to watch
Shift+Arrow Extend by one cell Focused cell; then range after settle None in focus mode
Ctrl+Shift+Arrow Extend to data edge Range after settle NVDA uses Ctrl+Alt+Arrow for table navigation, not this
Shift+Space Select row “Build row selected.” JAWS: in browse mode Shift+Space may be consumed
Ctrl+Space Select column “Wed column selected.” OS input-method switch on some locales
Ctrl+A Select all “All 20 cells selected.” Browser select-all if not prevented
Escape Collapse “Selection cleared.” Must not also close a surrounding dialog
Anchor handling, wrong and right Comparison of moving the anchor with every Shift+Arrow press against keeping a fixed anchor and moving only the focus corner. Anchor handling, wrong and right✗ Anchor follows focusEvery Shift+Arrow resets the start pointSelection is always two cellsReversing direction behaves unpredictablyRange announcements are meaningless✓ Fixed anchorAnchor set on the first Shift+ArrowFocus corner moves; rectangle grows orshrinksReversing direction shrinks the rangePlain arrow or Escape clears the anchor
The anchor is state the grid must remember between keypresses — it is not derivable from focus.

Integration context

Permalink to "Integration context"

Row-selection grids with checkboxes have their own range gesture, Shift+Click with a Shift+Space equivalent, covered in shift-click range selection with keyboard equivalents. Whichever model you use, write the keys into the grid’s help dialog as described in keyboard shortcut help dialogs.

In a virtualised grid, Ctrl+Shift+Arrow to the data edge may land on a row that is not rendered; scroll it into view and render it before moving focus, or focus is lost.

Selection keys by platform Matrix mapping each selection action to its key on Windows and Linux and on macOS, with a note on conflicts. Selection keys by platformActionWindows / LinuxmacOSConflictExtend by oneShift+ArrowShift+ArrowNone in focus modeExtend to edgeCtrl+Shift+ArrowCmd+Shift+ArrowCtrl+Arrow is Mission ControlSelect columnCtrl+SpaceCtrl+SpaceIME switching on some localesSelect allCtrl+ACmd+APrevent page select-all
Accept both Ctrl and Cmd where the action is universal, and give macOS users an edge-extension key that Mission Control does not claim.

Gotchas

Permalink to "Gotchas"

Selection surviving sort. If the user sorts while a range is selected, the rectangle now covers different data. Clear the selection on sort and say so.

Edit mode. Shift+Arrow inside a cell editor selects text in the input. The grid must ignore selection keys while a cell is in edit mode, as in entering and exiting cell edit mode.

macOS modifiers. Mac users expect Cmd+A, not Ctrl+A, and Ctrl+Arrow is often bound to Mission Control. Accept metaKey for select-all, and offer Cmd+Shift+Arrow or Shift+End for edge extension.

Design system notes

Permalink to "Design system notes"

Selection keys should be implemented once, in the grid primitive, and configured rather than reimplemented. Expose which shortcuts are enabled (rowSelectKey, columnSelectKey, selectAllKey) so products can switch off Ctrl+Space where it collides with input methods, and generate the keyboard help from the same configuration, so documentation and behaviour cannot disagree.

Testing checklist

Permalink to "Testing checklist"

FAQ

Permalink to "FAQ"
Should Shift+click and Shift+Arrow share the same anchor?

Yes. Pointer and keyboard selection should use one anchor-and-focus model, so a user can click a cell, then Shift+Arrow from it, or start with the keyboard and finish with Shift+click, and get the rectangle they expect.

What keys extend a selection in an accessible data grid?

Shift with the arrow keys extends the selection one cell at a time from a fixed anchor. Shift+Space selects the row, Ctrl+Space the column, Ctrl+A everything, and Escape or a plain arrow collapses the selection.

Do these shortcuts conflict with screen reader keys?

Not in focus mode, which a role=“grid” normally triggers. In browse mode the screen reader consumes arrow keys and some Shift combinations, which is why selection keys belong in grids and not in static tables.

What should happen to the selection when the user presses a plain arrow key?

The selection collapses to nothing and focus moves one cell, just as in a text editor. The anchor is forgotten, so the next Shift+Arrow starts a new range.

Permalink to "Related"

← Back to Cell Selection & Clipboard in Data Grids