aria-owns
Declares a parent-child relationship in the accessibility tree when the DOM structure does not express it. Reparents the referenced elements as children of this one for AT purposes. A power tool — easy to misuse.
When to use
Only when the DOM cannot put the children where the accessibility tree needs them. The most common case is a popup listbox for a combobox: the list lives at the end of <body> for z-index reasons, but semantically it belongs to the combobox. aria-owns on the combobox, pointing at the listbox’s ID, tells AT to treat the listbox as a child.
If you can restructure the DOM so the child is a real descendant, do that first. aria-owns is the escape hatch — it works, but it bypasses every author’s natural sanity check that the markup matches the semantics.
How it behaves
The value is a space-separated list of element IDs. The accessibility tree is rebuilt so each referenced element appears as a child of the owning element, in the order listed. The original DOM parent loses those children in the AT view.
Rules to keep the tree valid:
- Each referenced ID must exist and be unique.
- An element can only be
aria-owns-ed by one parent at a time. Owning the same element from two places produces undefined behaviour. - Do not own an ancestor of yourself. That creates a cycle in the AT tree.
- The element must still be reachable in the DOM —
display: noneis allowed, but a fully detached element is not.
Common failures
- Pointing at an ID that does not exist or has been removed in the latest render.
- Using
aria-ownswhen the DOM could simply nest the children correctly. Adds complexity without benefit. - Two
aria-ownsreferences for the same child from different parents — the AT picks one arbitrarily. - Forgetting that focus order is unrelated to
aria-owns. The keyboard still tabs through the visual DOM order; the AT tree only affects how screen readers traverse the accessibility hierarchy. - Reaching for
aria-ownsto solve a focus or scroll-into-view problem. It does neither. - Owning a region that contains its own owner — creates a cycle, breaks the tree.
Example
<!-- Combobox whose popup is portalled to body for stacking context -->
<div
role="combobox"
id="city-cb"
aria-haspopup="listbox"
aria-expanded="true"
aria-owns="city-listbox"
>
<input type="text" aria-controls="city-listbox" aria-activedescendant="city-opt-2">
</div>
<!-- Listbox lives at the end of body, owned semantically by the combobox -->
<ul role="listbox" id="city-listbox">
<li role="option" id="city-opt-1">London</li>
<li role="option" id="city-opt-2" aria-selected="true">Paris</li>
</ul>