Standards · ARIA

Property Widget state

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 a role="menu" (the most common case for menu buttons).
  • "listbox" — opens a role="listbox", used in custom comboboxes.
  • "tree" — opens a role="tree".
  • "grid" — opens a role="grid" (datepickers often).
  • "dialog" — opens a role="dialog" or role="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-haspopup to a control that does not actually open a popup. Misleads the user, who hears the affordance and finds nothing.
  • Using aria-haspopup on 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 dialog popup or down into the first menuitem of a menu. The attribute names the popup type, but does not manage focus.
  • Setting aria-haspopup="dialog" but failing to set role="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>