Selecting Entire Rows and Columns from the Keyboard
Permalink to "Selecting Entire Rows and Columns from the Keyboard"In spreadsheet-like grids, users often need whole rows or columns: copy a column of amounts, delete three rows, format a column. Mouse users click row numbers or column letters. Keyboard users need equivalents, and spreadsheet conventions provide them: Shift+Space selects the row, Ctrl+Space selects the column. These shortcuts conflict with little else inside a grid, but they need clear feedback — selecting a column of 5,000 cells must not be silent.
This guide belongs to grid cell selection & clipboard.
Spec reference
Permalink to "Spec reference"aria-selectedon gridcells (cell selection) or rows (row selection);aria-multiselectable="true"on the grid.- SC 2.1.1 (A) — selection without a pointer.
- SC 4.1.3 (AA) — selection size as a status message.
- SC 1.4.1 (A) — selected cells distinguished by more than colour.
- Spreadsheet conventions — Shift+Space (row), Ctrl+Space (column), Ctrl+A (all).
When to use — and when not to
Permalink to "When to use — and when not to"Support row and column selection in grids with cell-level selection (spreadsheets, data editors). In record-oriented grids with row checkboxes, row selection via Space on the checkbox is enough; column selection rarely makes sense.
Do not use Ctrl+Space if your grid sits in an environment where it switches input methods (some CJK setups) without offering an alternative (a header button). The misapplication: selecting a column with a single keystroke and no announcement, followed by Delete clearing 5,000 cells silently.
Annotated code example
Permalink to "Annotated code example"grid.addEventListener('keydown', (e) => {
const { row, col } = active();
if (e.key === ' ' && e.shiftKey && !e.ctrlKey) { // select row
e.preventDefault(); selectRange({ rows: [row, row], cols: [0, colCount - 1] });
status.textContent = `Row ${row + 1} selected, ${colCount} cells`; // SC 4.1.3
}
if (e.key === ' ' && (e.ctrlKey || e.metaKey) && !e.shiftKey) { // select column
e.preventDefault(); selectRange({ rows: [0, rowCount - 1], cols: [col, col] });
status.textContent = `Column ${headerName(col)} selected, ${rowCount} cells`;
}
});
function selectRange({ rows, cols }) {
selection = { rows, cols };
for (const cell of renderedCells()) {
const inRange = within(cell.row, rows) && within(cell.col, cols);
cell.el.setAttribute('aria-selected', String(inRange)); // rendered cells only; virtual rows read from state
}
}
[role="gridcell"][aria-selected="true"] { background: var(--selected-bg); box-shadow: inset 0 0 0 1px var(--brand-strong); } /* not colour only */
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Key | Action | Expected announcement |
|---|---|---|
| Shift+Space | Select row | “Row 3 selected, 8 cells” |
| Ctrl+Space | Select column | “Column Amount selected, 240 cells” |
| Shift+Down after row selection | Extend rows | “Rows 3 to 5 selected” |
| Shift+Right after column selection | Extend columns | “Columns Amount to Status selected” |
| Ctrl+A | Select all | “All 1,920 cells selected” |
| Escape | Clear selection | “Selection cleared” |
Integration context
Permalink to "Integration context"Range announcements follow announcing rectangular cell range selection; extending ranges in extending selection with Shift and arrow keys. Where to put aria-selected is covered in aria-selected on cells versus rows. Copying the selection uses keyboard copy and paste in data grids.
Gotchas
Permalink to "Gotchas"Virtualized cells. Only rendered cells carry aria-selected; keep selection in state and render it consistently as rows scroll in.
Ctrl on macOS. Spreadsheets on macOS use Ctrl+Space too (Cmd+Space is Spotlight); test both.
Destructive follow-ups. After large selections, Delete or paste should confirm or offer undo.
Worked example
Permalink to "Worked example"A budgeting grid supported cell selection only by mouse drag. Keyboard users could select single cells and Shift+arrow ranges but not whole columns; copying a 500-row column took minutes. Adding Shift+Space, Ctrl+Space, header buttons for row and column selection, selection announcements with sizes and undo for bulk clears let keyboard users copy columns in two keystrokes.
Common mistakes
Permalink to "Common mistakes"- No keyboard equivalent for header-click selection.
- Silent large selections.
- aria-selected not updated on virtualized rows.
- Colour-only selection styling.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Document these shortcuts in the grid’s keyboard contract and expose a selectionMode that enables them for spreadsheet-style grids only.
Step-by-step implementation
Permalink to "Step-by-step implementation"- Adopt spreadsheet conventions. Shift+Space selects the current row and Ctrl+Space selects the current column. Users who know Excel or Google Sheets expect these, and screen readers in application mode pass them through.
- Mark selected cells. Set
aria-selected="true"on every selected cell (and on the row for row selection) and make surearia-multiselectable="true"is on the grid. - Announce the selection size. After selecting a column, announce “Column Amount selected, 240 cells”. Screen readers do not compute this for you, and without it users cannot confirm what they selected.
- Extend with Shift+Arrow. After a row selection, Shift+Down extends to the next row; after a column selection, Shift+Right extends to the next column. Announce the new range each time.
- Expose the selection to commands. Copy, delete and fill should operate on the selection and say what they did: “Copied 240 cells”.
Edge cases
Permalink to "Edge cases"- Virtualized grids. Selecting a whole column must select rows that are not rendered; keep the selection as a range, not a list of DOM nodes.
- Conflicts with the screen reader. Shift+Space can be captured by some screen readers in browse mode; the grid must be in focus mode, which
role="grid"normally triggers. - Header cells. Selecting a column from its header should select data cells, not the header itself.
Testing it with a screen reader
Permalink to "Testing it with a screen reader"Start in NVDA with Firefox and make sure the grid is in focus mode. Move to a cell, press Shift+Space and listen for a row selection message with a cell count. Press Ctrl+Space and confirm the column selection message. Extend with Shift+Arrow and check that each step updates the announced range. Then press Ctrl+C and verify the “Copied” message and the clipboard content in a text editor. In JAWS, confirm that the keys are passed through to the grid; if JAWS intercepts them, document the pass-through key. In VoiceOver, check that aria-selected is announced as “selected” when moving onto selected cells, since that is how users verify the selection later.
FAQ
Permalink to "FAQ"Should row selection select the row or its cells?
In cell-selection grids, select cells in the row; in row-selection grids, set aria-selected on the row.
How do I announce very large selections?
Give counts and ranges, not cell lists.
Can headers be buttons for selection?
Yes; a small “Select column” button in each header or row number cell works for pointer and keyboard.
What if Ctrl+Space conflicts with an IME?
Offer header buttons and an alternative shortcut documented in help.
Should Escape clear the selection?
Yes, in navigation mode; announce “Selection cleared”.
How does this interact with sorting?
Clear or remap selections when the order changes, and announce it.
Related
Permalink to "Related"- Announcing rectangular range selection — messages
- aria-selected on cells versus rows — where state lives
- Spreadsheet-style editing grids — the context