invoketarget: when HTML finally handles its own interactions
Invoker Commands are arriving in Chrome. invoketarget and invokeaction let a button target a popover or dialog without any JavaScript event listener. Here's what the pattern looks like and why it matters.
The smallest JavaScript component you’ve ever been asked to write:
document.getElementById('open-btn').addEventListener('click', () => {
document.getElementById('my-dialog').showModal();
});
No logic. No transformation. No side effect. Just “button clicked, dialog opened.” The HTML already knows about both elements. The relationship is self-evident. And yet: JavaScript.
Invoker Commands — shipping in Chrome 135 — let you express that relationship in HTML directly:
<button invoketarget="my-dialog" invokeaction="showModal">
Open dialog
</button>
<dialog id="my-dialog">
<h2>You didn't need JavaScript to open this.</h2>
<button invoketarget="my-dialog" invokeaction="close">Close</button>
</dialog>
No JavaScript. No event listener. The button targets the dialog by ID and specifies the action. The browser makes the connection.
Browser support: Chrome 135+ (April 2025). Firefox: expressed support, not yet landed. Safari: no public position. Check MDN for current status. Use the feature detection pattern in the progressive enhancement section below.
The actions
For <dialog>:
invokeaction="showModal"— opens as a modal with backdropinvokeaction="show"— opens as non-modalinvokeaction="close"— closes the dialog
For [popover]:
invokeaction="togglePopover"— toggles open/closed (this is whatpopovertargetalready does)invokeaction="showPopover"— opensinvokeaction="hidePopover"— closes
Custom elements can handle invoke events too:
myWidget.addEventListener('invoke', (event) => {
if (event.action === 'increment') {
this.value++;
}
});
Any custom element can become an invokable target, with buttons anywhere in the DOM triggering specific named actions.
Why this matters beyond convenience
The boring argument: less JavaScript. Fewer event listeners. Smaller bundles.
The more interesting argument: HTML declarative relationships are serializable, inspectable and accessible by default. When a button’s relationship to its target is expressed in HTML attributes, assistive technologies can understand that relationship structurally — “this button opens the dialog” — from the markup, rather than inferring it from runtime behavior.
JavaScript wiring makes the relationship implicit: buried in event listeners, closures and runtime state. Invoker Commands make it explicit and structural.
This is the same argument that justified <details>/<summary>, <dialog>, [popover] and every native HTML interactive element: when the browser understands the interaction model, it can expose it to accessibility APIs, implement keyboard handling correctly and maintain state without JavaScript tracking it.
Progressive enhancement
<button id="open-btn" invoketarget="my-dialog" invokeaction="showModal">
Open
</button>
// Fallback for browsers without Invoker Commands support
if (!('invokeTargetElement' in HTMLButtonElement.prototype)) {
document.getElementById('open-btn').addEventListener('click', () => {
document.getElementById('my-dialog').showModal();
});
}
The six lines of JavaScript that wired a button to a dialog will eventually be unnecessary. Not yet. But the direction is clear.
The browser is getting better at handling its own interactions. The JavaScript we write is, slowly, becoming the JavaScript we actually need to write.