command
and commandfor
: the Invoker Commands API
Popovers and dialogs have landed in every modern browser, but a new API may change how we create them. The Invoker Commands API lets us create modal dialogs and popovers without scripting. Invoker Commands also let us create custom commands using declarative markup and a touch of JavaScript.
Support for the Invoker Commands API is available in Chrome and Edge as of version 135, Opera as of version 120, and Safari Technical Preview. It's also available in Firefox Nightly behind the dom.element.commandfor
feature flag.
commandfor
and command
attributes
Central to the Invoker Commands API are the commandfor
and command
HTML attributes. They're sort of generic analogues of the popovertarget
and popovertargetaction
attributes that also let us manage dialog elements.
Like popovertarget
, the value of the commandfor
attribute should be the id
of a target element.
<button
type="button"
commandfor="targetElement">Trigger</button>
The command
attribute indicates the action to perform when the user interacts with the command trigger. Its value can be a custom command keyword, prefixed with two hyphen characters --
, or one of the following:
show-popover
- Shows a popover that is currently hidden. Works the same way as
popovertargetaction="show"
. hide-popover
- Hides an popover that is currently visible. Works the same way as
popovertargetaction="hide"
. toggle-popover
- Toggles a popover between its showing and hidden states. Equivalent to
popovertargetaction="toggle"
. show-modal
- Shows dialog elements as a modal. Equivalent of invoking the
dialog.showModal()
method, but declaratively in HTML instead of with scripting. close
- Closes dialog elements. Same as invoking
dialog.close()
. request-close
1- Requests the closure of a
dialog
element. Does nothing if the dialog is already closed. Same as callingdialog.requestClose()
.
For popovers, your target element still needs to have a popover
attribute.
<button
type="button"
commandfor="targetElement"
command="toggle-popover"
>Trigger</button>
<div id="targetElement" popover>…</div>
For modal-style dialogs, use a dialog
element. Add a close trigger for each dialog.
<button
type="button"
commandfor="targetElement"
command="show-modal"
>Show dialog</button>
<dialog id="targetElement">
<button
type="button"
commandfor="targetElement"
command="close"
>Hide dialog</button>
…
</dialog>
Custom commands
The Invoker Commands API also supports custom commands. Custom commands do require a bit of scripting, but they also make a little easier to build and maintain UI components.
Custom commands are declared with a custom value, prefixed by two hyphen characters. The example below creates a --flip
command.
<button
type="button"
commandfor="targetElement"
command="--flip"
>Trigger</button>
<div id="targetElement">Flip target</div>
Clicking the button triggers a CommandEvent
. Command events inherit from the Event
interface, and includes two additional properties:
source
-
- The button element that triggered the command event.
command
- The command that to invoke.
To handle command events, add an event listener to the target element. Here's an example using button
elements to create custom video controls.
First the HTML.
<video src="movie.mp4" id="vid" type="video/mp4"></video>
<button
commandfor="vid"
command="--play">Play</button>
<button
commandfor="vid"
command="--pause">Pause</button>
<button
commandfor="vid"
command="--mute">Mute</button>
<button
commandfor="vid"
command="--unmute">Unmute</button>
Each button has a commandFor
attribute that matches the id
of the video element, and a command
attribute indicating the action the button invokes. The event listener listens for command events on the target element, and responds to commands as indicated.
const vidPlayer = document.getElementById("vid");
vidPlayer.addEventListener( 'command', (event) => {
const { command, target, type } = event;
if( type !== 'command' ) return;
switch( command ) {
case '--pause':
target.pause();
break;
case '--mute':
target.volume = 0;
break;
case '--unmute':
target.volume = 1;
break;
default:
target.play();
}
});
Again: we listen for ComandEvent
gets fired on the video
element, instead of listening for click
or touch
events on each button
element.
Conclusion
Having a browser-native, declarative API greatly reduces the need for JavaScript interaction libraries. Invoker commands mean that users don't have to wait for JavaScript to download before these controls become interactive. Custom invokers also keep us from abusing data-*
attributes.
Specifications and more resources
command
attribute documentation.commandFor
attribute documentation- CommandEvent interface
- Invoker Commands API from mdn Web Docs
- Introducing command and commandfor from the Chrome for Developers blog
Genie lamp illustration by j4p4n from OpenClipArt.
-
You may be unfamiliar with the
requestClose()
method. It's a newly-available part of theHTMLDialogElement
API (as of May 2025). It adds a mechanism for canceling the closure of a modal. ↩