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.
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 |
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.
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.
Related
Permalink to "Related"- Announcing cell ranges — what to say when the gesture settles
- Shift-click range selection — the row-selection version
- Keyboard shortcut help dialogs — documenting the contract