Standards · ARIA

Property Relationship

aria-activedescendant

On a composite widget, points to the ID of the descendant that is currently active. DOM focus stays on the container while a virtual focus moves across children. Alternative to roving tabindex for listboxes, comboboxes, and grids.

When to use

On a composite widget where you want a single tab stop but the user still needs to move a highlight across many items — combobox input with a listbox popup, single-tab-stop listbox, treegrid, datagrid. DOM focus stays on the container element (the input, the listbox, the grid). As the user presses arrow keys, you update aria-activedescendant to the ID of the newly highlighted child, and AT announces it as if focus had moved there.

The alternative pattern is roving tabindex: actually move DOM focus and adjust tabindex values. Pick aria-activedescendant when keeping browser focus on the container simplifies your code — typically because the container is also where the user types or where keyboard shortcuts attach.

How it behaves

The value is a single element ID — never a list. The referenced element must be a descendant of the container that has the aria-activedescendant attribute (or owned via aria-owns). When the value changes, the accessibility-tree position of the “virtual focus” moves to that element and AT announces its label, role, and state.

Two things you still own:

  • Visual styling. AT knows where the virtual focus is, but the browser does not paint a focus ring on the descendant. Add your own highlight CSS, often via a class toggled in sync.
  • Scrolling. If the active descendant is below the fold, you must scroll it into view; the browser will not.

When the popup closes or the widget loses focus, clear aria-activedescendant to an empty string or remove the attribute.

Common failures

  • Pointing at an ID that is not a descendant (and is not owned via aria-owns). The AT ignores the reference.
  • Mixing patterns: applying both tabindex="0" to the descendant and setting aria-activedescendant on the container. Pick one focus strategy; doing both fights itself.
  • Forgetting to update the visual highlight when the attribute changes — sighted users see no movement while AT users hear the move.
  • Leaving a stale ID after the option is removed from the list (filtered combobox results, for instance).
  • Updating aria-activedescendant on mouse hover. The attribute should track the keyboard-driven active item only.
  • Setting it on an element that does not contain the descendant in the AT tree (forgot aria-owns for a portalled list).

Example

<label for="fruit">Fruit</label>
<input
  id="fruit"
  role="combobox"
  type="text"
  aria-controls="fruit-listbox"
  aria-expanded="true"
  aria-activedescendant="fruit-2"
  autocomplete="off"
>
<ul id="fruit-listbox" role="listbox">
  <li id="fruit-1" role="option">Apple</li>
  <li id="fruit-2" role="option" class="is-active">Banana</li>
  <li id="fruit-3" role="option">Cherry</li>
</ul>