Expand All and Collapse All Controls

Permalink to "Expand All and Collapse All Controls"

Treegrids and grouped tables often need bulk expansion: open every group to scan everything, or collapse all to see the overview. Keyboard users can expand nodes one by one (Right arrow or Enter on each), which is slow; “Expand all” and “Collapse all” buttons help everyone. Their accessibility challenges are scope (all groups or all levels?), feedback (what changed?), and focus (what happens to a focused row that collapse hides?).

This guide belongs to expandable rows & nested data.

Spec reference

Permalink to "Spec reference"
  • ARIA treegrid pattern — individual rows use aria-expanded; there is no ARIA attribute for “expand all”, so it is an ordinary button.
  • The asterisk (*) key in the tree pattern expands all siblings at the current level — a useful keyboard shortcut to support.
  • SC 4.1.3 (AA) — how many rows appeared or disappeared is a status message.
  • SC 2.4.3 (A) — focus must not be lost when collapse hides the focused row.
Collapse all with a focused child row Chain: focus is on a child row; the user activates Collapse all from the toolbar; groups collapse; the focused row is hidden, so the grid's active row moves to its parent; the status announces how many rows were hidden. Collapse all with a focused child rowFocus on childrow"INV-1042, level 2"Groups collapse240 rows hiddenParent rowactivegrid's tab stopmoves to "Lund & Co,level 1"Announcement"All groupscollapsed. 12 groupsshown."Collapse allremembered positionstatus
The grid's remembered position moves to a visible ancestor.

When to use — and when not to

Permalink to "When to use — and when not to"

Provide expand and collapse all when there are more than a handful of groups or tree levels. Name scope precisely: “Expand all groups” or “Expand all levels”.

Do not offer expand all on very large or lazy-loaded trees without limits; expanding 10,000 nodes freezes the page and overwhelms screen reader buffers. Offer “Expand all groups (first level)” instead, or warn and confirm.

The misapplication: a single icon toggling between expand and collapse, with an unchanged name (“Toggle”), leaving screen reader users unsure which happened.

One toggle icon versus two named buttons Comparison: a single icon toggle with a static name hides state; two named buttons (or a toggle whose name changes) make the action and its result clear. One toggle icon versus two named buttons✗ Single "Toggle" iconName unchanged after activationState unclearNo feedbackIcon meaning varies✓ Two named buttons"Expand all groups" and "Collapse all groups"Each action explicit"12 groups expanded, 240 rows shown"Text labels

Annotated code example

Permalink to "Annotated code example"
<div role="toolbar" aria-label="Invoice groups" aria-controls="inv-treegrid">
  <button type="button" id="expand-all">Expand all groups</button>
  <button type="button" id="collapse-all">Collapse all groups</button>
</div>
<p id="tg-status" role="status" class="visually-hidden"></p>
expandAll.addEventListener('click', async () => {
  const before = visibleRowCount();
  await treegrid.expandLevel(1);                         // first level only; lazy children load as needed
  status.textContent = `All ${groupCount()} groups expanded. ${visibleRowCount() - before} rows shown.`;  // SC 4.1.3
  // focus stays on the button (no focus move)
});

collapseAll.addEventListener('click', () => {
  const active = treegrid.activeRow();
  treegrid.collapseAll();
  if (active && !treegrid.isVisible(active)) treegrid.setActiveRow(treegrid.visibleAncestor(active));  // SC 2.4.3
  status.textContent = `All groups collapsed. ${groupCount()} groups shown.`;
});

// Asterisk on a row expands all siblings at that level (tree pattern)
treegridEl.addEventListener('keydown', (e) => {
  if (e.key === '*') { e.preventDefault(); treegrid.expandSiblings(treegrid.activeRow()); }
});

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Interaction Expected Announcement
Activate Expand all groups Groups open; focus stays “All 12 groups expanded. 240 rows shown.”
Activate Collapse all groups Groups close “All groups collapsed. 12 groups shown.”
Tab back into grid after collapse Active row is a visible ancestor Row name and level
* on a row Siblings expand Each row’s expanded state when visited
Expand-all options by tree size Matrix of tree sizes — small, medium, large, lazy-loaded — against expand all, expand first level, asterisk for siblings and confirmation. Expand-all options by tree sizeExpand allFirst level onlyAsterisk siblingsConfirm firstSmall (under 200 rows)YesOptionalYesNoMedium (200 to 2,000)With careYesYesOptionalLarge (over 2,000)AvoidYesYesYesLazy-loadedAvoidYes, loads onelevelYesYes
Scale the control to the data.

Integration context

Permalink to "Integration context"

Individual expansion behaviour is covered in collapsing row groups with aria-expanded and building an accessible treegrid with expandable rows. Lazy children follow lazy loading child rows in a treegrid. Toolbars follow the toolbar pattern for grid actions.

Gotchas

Permalink to "Gotchas"

Announcement length. Do not list expanded groups; give counts.

Scroll position. Expanding all can push the user’s place far down; keep the previously active row in view if focus returns to the grid.

Screen reader buffers. Expanding thousands of rows forces NVDA and JAWS to rebuild their buffers; expect a pause and prefer level-limited expansion.

Worked example

Permalink to "Worked example"

A project portfolio treegrid had 30 programmes with 400 projects. A single “toggle all” icon expanded everything, freezing the page for two seconds and leaving JAWS users unsure what happened. The team replaced it with “Expand all programmes” and “Collapse all programmes” buttons, limited expansion to the first level, supported the asterisk key, moved the active row to a visible ancestor on collapse and announced counts. Performance and clarity both improved.

Common mistakes

Permalink to "Common mistakes"
  • An unnamed toggle icon.
  • Expanding all levels of huge trees.
  • Losing the active row on collapse.
  • Listing every expanded group in the announcement.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Add expand and collapse all to the treegrid toolbar slot with configurable scope and built-in messages; support the asterisk key in the grid’s keyboard contract.

Step-by-step implementation

Permalink to "Step-by-step implementation"
  1. Place the controls above the table. “Expand all” and “Collapse all” are two buttons near the table caption, not hidden in a header cell. Name them with the scope: “Expand all 48 groups”.
  2. Keep per-row state consistent. After expand all, every row toggle must report aria-expanded="true". Drive both the bulk action and individual toggles from the same state store so they cannot disagree.
  3. Keep focus on the button. After the action, focus stays on the button that was pressed. Moving focus into the newly expanded rows would disorient users who wanted to scan from the top.
  4. Announce the result. A polite message such as “48 groups expanded, 312 rows shown” tells screen reader users the table got longer, which they cannot otherwise perceive.
  5. Limit the cost. For large trees, expand lazily: render the first level of children immediately and fetch deeper levels on demand, showing a progress status rather than freezing the page.

Edge cases

Permalink to "Edge cases"
  • Mixed state. When some groups are expanded, both buttons stay enabled; do not use a single toggle whose label flips unpredictably.
  • Filtered views. Expand all should affect only visible groups, and the announcement should say so.
  • Virtualization. Expanding everything can push the focused row far away; keep the scroll position anchored to the button.

Testing it with a screen reader

Permalink to "Testing it with a screen reader"

With NVDA, tab to “Expand all 48 groups” and press Enter. You should hear the polite status message with the number of groups and rows, and focus should remain on the button. Arrow into the table and confirm that each group toggle now reports “expanded”. Return to “Collapse all”, activate it and check the matching message. In VoiceOver, repeat with the rotor’s table navigation to confirm that collapsed rows are no longer reachable. Pay attention to timing on large data sets: if expansion takes more than a second, a “Loading groups…” status should appear first, so the user knows the action is in progress rather than ignored.

FAQ

Permalink to "FAQ"
Should Expand all move focus into the grid?

No. Keep focus on the button so users can choose what to do next.

Should the state persist?

Remember expansion per user if they return often; announce restored state only if it differs from defaults.

Is one toggle button acceptable?

Yes, if its name changes to reflect the next action (“Collapse all groups” after expanding), or use aria-pressed with a stable name.

How do I handle partially expanded trees?

Expand all expands everything in scope; Collapse all collapses everything. Mixed states need no special handling.

Does the asterisk key conflict with screen readers?

In focus mode inside a treegrid, keys pass to the page; document it in help.

Should groups remember individual state after collapse all and expand all?

Expand all should expand everything regardless of previous state; that is what users expect.

Permalink to "Related"

← Back to Expandable Rows & Nested Data in Accessible Tables