Invoker commands
Wire a button to open a popover, close a dialog, or run a custom action declaratively with command and commandfor — no click handler, no ARIA plumbing.
What it is
Invoker commands let a <button> control another element declaratively, through two HTML attributes:
commandforreferences theidof the element to act on.commandnames the action to perform.
The browser wires the click and keyboard activation, dispatches a command event on the target, and runs the built-in behaviour — no addEventListener, no aria-controls, no aria-expanded to keep in sync.
<button commandfor="menu" command="toggle-popover">Open menu</button>
<div id="menu" popover>
<button commandfor="menu" command="hide-popover">Close</button>
</div>
The built-in commands cover the common overlay patterns: show-popover, hide-popover, toggle-popover for popovers, and show-modal, close, request-close for <dialog>. Any value prefixed with two dashes (for example command="--rotate") is a custom command: the browser fires a CommandEvent on the target carrying that name, and your script decides what to do — invoker wiring without a built-in action.
Why it matters
- Less JavaScript for common patterns. Opening a popover or closing a dialog no longer needs a click handler. The relationship lives in markup the browser understands.
- Accessibility comes for free. The control is a real
<button>, so keyboard activation, focus, and the exposed relationship are handled natively. There is no ARIA to add or to drift out of sync. - Custom commands are properly delegated. A
--namecommand dispatches a single typed event from a real button, replacing ad-hoconclickwiring with one consistent mechanism — and you can readevent.sourceto know which button invoked it. - It composes with the platform. Invoker commands pair directly with the Popover API,
<dialog>, and native interactive elements rather than re-implementing their behaviour in script.
How to implement
Reach for invoker commands whenever a button’s job is to drive another element. Use the built-in popover and dialog commands for overlays; use a ---prefixed custom command when you want the declarative button-to-target wiring but a behaviour the platform does not ship.
<button commandfor="confirm" command="show-modal">Delete…</button>
<dialog id="confirm">
<form method="dialog">
<p>Delete this item?</p>
<button command="close" commandfor="confirm">Cancel</button>
<button value="delete">Delete</button>
</form>
</dialog>
Treat it as progressive enhancement where you still support older browsers: a button with a JavaScript fallback degrades to a normal control.
Common mistakes
- Putting
command/commandforon a non-button. They only work on<button>. A<div>with these attributes does nothing and is not keyboard-operable. - Custom command without a listener. A
--namecommand does nothing on its own; you must handle thecommandevent on the target. - Forgetting
request-close. For dialogs that should run cancel behaviour and fire thecancelevent, preferrequest-closeoverclose.
Verification
- Activate the button with the keyboard (Enter/Space): the target popover or dialog responds without any script.
- In DevTools, confirm the
<button>exposes the command relationship and that acommandevent fires on the target. - Baseline: invoker commands became newly available across major browsers at the end of 2025 — keep a scripted fallback if you still support older versions.