aria-haspopup
Tells assistive tech that activating this control will open a popup, and what kind. Accepts "menu", "listbox", "tree", "grid", "dialog", or "true" / "false". Pair with aria-expanded so AT can also announce whether the popup is open.
When to use
On any control that opens a transient overlay — a button that triggers a menu, an input that opens a listbox in a combobox, an icon button that pops a dialog. The attribute lets the screen reader announce something like “Filter, menu button” or “Search, has popup”, so the user knows pressing the control will reveal more UI rather than navigate or submit.
Skip it for controls that just toggle a section of the same page — a disclosure widget should use aria-expanded on its own; the revealed content is part of the page, not a popup.
How it behaves
Accepts a fixed set of token values:
"menu"— opens arole="menu"(the most common case for menu buttons)."listbox"— opens arole="listbox", used in custom comboboxes."tree"— opens arole="tree"."grid"— opens arole="grid"(datepickers often)."dialog"— opens arole="dialog"orrole="alertdialog"."true"— equivalent to"menu"for legacy reasons; prefer one of the specific values."false"— explicit “does not have a popup”; the default.
Pair aria-haspopup with aria-expanded="true|false" so AT can also announce whether the popup is currently open. The two attributes do different jobs — haspopup says “there is a popup”, expanded says “and it is currently visible”.
Common failures
- Using
aria-haspopup="true"on a button that opens a custom date picker (which is really a grid or dialog) — AT announces “menu button” and users press the down-arrow expecting menuitems. - Forgetting
aria-expanded. AT says “has popup” but never announces the open/closed state, so users cannot tell if their click did anything. - Adding
aria-haspopupto a control that does not actually open a popup. Misleads the user, who hears the affordance and finds nothing. - Using
aria-haspopupon a link that navigates to a new page. It’s not a popup; just let the link be a link. - Forgetting to move focus into a
dialogpopup or down into the firstmenuitemof amenu. The attribute names the popup type, but does not manage focus. - Setting
aria-haspopup="dialog"but failing to setrole="dialog"on the popup. The promised semantics never appear.
Example
<!-- Menu button -->
<button
type="button"
id="filter-btn"
aria-haspopup="menu"
aria-expanded="false"
aria-controls="filter-menu"
>
Filter
</button>
<ul id="filter-menu" role="menu" hidden>
<li role="menuitem">All</li>
<li role="menuitem">Unread</li>
<li role="menuitem">Flagged</li>
</ul>