The Popover API and <dialog>
ingredient are two of my favourite new platform options. In truth, I lately [wrote a detailed overview of their use cases] and the types of issues you are able to do with them, even studying a couple of methods within the course of that I couldn’t discover documented anyplace else.
I’ll admit that one factor that I actually dislike about popovers and dialogs is that they may’ve simply been mixed right into a single API. They cowl completely different use instances (notably, dialogs are usually modal) however are fairly comparable in apply, and but their implementations are completely different.
Effectively, internet browsers are actually experimenting with two HTML attributes — technically, they’re known as “invoker instructions” — which might be designed to invoke popovers, dialogs, and additional down the road, every kind of actions with out writing JavaScript. Though, for those who do attain for JavaScript, the brand new attributes — command
and commandfor
— include some new occasions that we will hear for.
Invoker instructions? I’m positive you’ve gotten questions, so let’s dive in.
We’re in experimental territory
Earlier than we get into the weeds, we’re coping with experimental options. To make use of invoker instructions right this moment in November 2024 you’ll want Chrome Canary 134+ with the enable-experimental-web-platform-features
flag set to Enabled
, Firefox Nightly 135+ with the dom.ingredient.invokers.enabled
flag set to true
, or Safari Know-how Preview with the InvokerAttributesEnabled
flag set to true
.
I’m optimistic we’ll get baseline protection for command
and commandfor
in due time contemplating how properly they summary the sort of work that at present takes a hefty quantity of scripting.
command
and commandfor
utilization
Fundamental First, you’ll want a <button>
or a button-esque <enter>
alongside the traces of <enter sort="button">
or <enter sort="reset">
. Subsequent, tack on the command
attribute. The command
worth ought to be the command title that you really want the button to invoke (e.g., show-modal
). After that, drop the commandfor
attribute in there referencing the dialog or popover you’re focusing on by its id
.
<button command="show-modal" commandfor="dialogA">Present dialogA</button>
<dialog id="dialogA">...</dialog>
On this instance, I’ve a <button>
ingredient with a command
attribute set to show-modal
and a commandfor
attribute set to dialogA
, which matches the id
of a <dialog>
ingredient we’re focusing on:
Let’s get into the attainable values for these invoker instructions and dissect what they’re doing.
Trying nearer on the attribute values
The show-modal
worth is the command that I simply confirmed you in that final instance. Particularly, it’s the HTML-invoked equal of JavaScript’s showModal()
technique.
The principle profit is that show-modal
allows us to, effectively… present a modal with out reaching straight for JavaScript. Sure, that is virtually an identical to how HTML-invoked popovers already work with thepopovertarget
and popovertargetaction
attributes, so it’s cool that the “stability is being redressed” as the Open UI explainer describes it, much more so as a result of you need to use the command
and commandfor
invoker instructions for popovers too.
There isn’t a present
command to invoke present()
for creating non-modal dialogs. I’ve talked about earlier than that non-modal dialogs are redundant now that we have now the Popover API, particularly since popovers have ::backdrop
s and different dialog-like options. My daring prediction is that non-modal dialogs will likely be quietly phased out over time.
The shut
command is the HTML-invoked equal of JavaScript’s shut()
technique used for closing the dialog. You most likely might have guessed that based mostly on the title alone!
<dialog id="dialogA">
<!-- Shut #dialogA -->
<button command="shut" commandfor="dialogA">Shut dialogA</button>
</dialog>
show-popover
, hide-popover
, and toggle-popover
values
The <button command="show-popover" commandfor="id">
…invokes showPopover()
, and is similar factor as:
<button popovertargetaction="present" popovertarget="id">
Equally:
<button command="hide-popover" commandfor="id">
…invokes hidePopover()
, and is similar factor as:
<button popovertargetaction="cover" popovertarget="id">
Lastly:
<button command="toggle-popover" commandfor="id">
…invokes togglePopover()
, and is similar factor as:
<button popovertargetaction="toggle" popovertarget="id">
<!-- or <button popovertarget="id">, since ‘toggle’ is the default motion anyway. -->
I do know all of this may be powerful to prepare in your thoughts’s eye, so maybe a desk will assist tie issues collectively:
command |
Invokes | popovertargetaction equal |
---|---|---|
show-popover |
showPopover() |
present |
hide-popover |
hidePopover() |
cover |
toggle-popover |
togglePopover() |
toggle |
So… yeah, popovers can already be invoked utilizing HTML attributes, making command
and commandfor
not all that helpful on this context. However like I mentioned, invoker instructions additionally include some helpful JavaScript stuff, so let’s dive into all of that.
Listening to instructions with JavaScript
Invoker instructions dispatch a command
occasion to the goal each time their supply button is clicked on, which we will hear for and work with in JavaScript. This isn’t required for a <dialog>
ingredient’s shut
occasion, or a popover
attribute’s toggle
or beforetoggle
occasion, as a result of we will already hear for these, proper?
For instance, the Dialog API doesn’t dispatch an occasion when a <dialog>
is proven. So, let’s use invoker instructions to hear for the command
occasion as a substitute, after which learn occasion.command
to take the suitable motion.
// Choose all dialogs
const dialogs = doc.querySelectorAll("dialog");
// Loop all dialogs
dialogs.forEach(dialog => {
// Hear for shut (as regular)
dialog.addEventListener("shut", () => {
// Dialog was closed
});
// Hear for command
dialog.addEventListener("command", occasion => {
// If command is show-modal
if (occasion.command == "show-modal") {
// Dialog was proven (modally)
}
// One other method to hear for shut
else if (occasion.command == "shut") {
// Dialog was closed
}
});
});
So invoker instructions give us extra methods to work with dialogs and popovers, and in some eventualities, they’ll be much less verbose. In different eventualities although, they’ll be extra verbose. Your method ought to depend upon what you want your dialogs and popovers to do.
For the sake of completeness, right here’s an instance for popovers, although it’s largely the identical:
// Choose all popovers
const popovers = doc.querySelectorAll("[popover]");
// Loop all popovers
popovers.forEach(popover => {
// Hear for command
popover.addEventListener("command", occasion => {
// If command is show-popover
if (occasion.command == "show-popover") {
// Popover was proven
}
// If command is hide-popover
else if (occasion.command == "hide-popover") {
// Popover was hidden
}
// If command is toggle-popover
else if (occasion.command == "toggle-popover") {
// Popover was toggled
}
});
});
Having the ability to hear for show-popover
and hide-popover
is helpful as we in any other case have to put in writing a type of “if opened, do that, else try this” logic from inside a toggle
or beforetoggle
occasion listener or toggle-popover
conditional. However <dialog>
parts? Yeah, these profit extra from the command
and commandfor
attributes than they do from this command
JavaScript occasion.
One other factor that’s obtainable to us through JavaScript is occasion.supply
, which is the button that invokes the popover
or <dialog>
:
if (occasion.command == "toggle-popover") {
// Toggle the invoker’s class
occasion.supply.classList.toggle("lively");
}
It’s also possible to set the command
and commandfor
attributes utilizing JavaScript:
const button = doc.querySelector("button");
const dialog = doc.querySelector("dialog");
button.command = "show-modal";
button.commandForElement = dialog; /* Not dialog.id */
…which is simply barely much less verbose than:
button.command = "show-modal";
button.setAttribute("commandfor", dialog.id);
Creating customized instructions
The command
attribute additionally accepts customized instructions prefixed with two dashes (--
). I suppose this makes them like CSS customized properties however for JavaScript occasions and occasion handler HTML attributes. The latter remark is perhaps a bit (or positively loads) controversial since utilizing occasion handler HTML attributes is taken into account unhealthy apply. However let’s check out that anyway, we could?
Customized instructions appear to be this:
<button command="--spin-me-a-bit" commandfor="document">Spin me a bit</button>
<button command="--spin-me-a-lot" commandfor="document">Spin me loads</button>
<button command="--spin-me-right-round" commandfor="document">Spin me proper spherical</button>
const document = doc.querySelector("#document");
document.addEventListener("command", occasion => {
if (occasion.command == "--spin-me-a-bit") {
document.fashion.rotate = "90deg";
} else if (occasion.command == "--spin-me-a-lot") {
document.fashion.rotate = "180deg";
} else if (occasion.command == "--spin-me-right-round") {
document.fashion.rotate = "360deg";
}
});
occasion.command
should match the string with the dashed (--
) prefix.
popover
and <dialog>
the one options that help invoker instructions?
Are In keeping with Open UI, invokers focusing on extra parts corresponding to <particulars>
had been deferred from the preliminary launch. I feel it is because HTML-invoked dialogs and an API that unifies dialogs and popovers is a must have, whereas different instructions (even customized instructions) really feel extra like a nice-to-have deal.
Nonetheless, based mostly on experimentation (I couldn’t assist myself!) internet browsers have really carried out extra invokers to various levels. For instance, <particulars>
instructions work as anticipated whereas <choose>
instructions match occasion.command
(e.g., show-picker
) however fail to really invoke the tactic (showPicker()
). I missed all of this at first as a result of MDN solely mentions dialog and popover.
Open UI additionally alludes to instructions for <enter sort="file">
, <enter sort="quantity">
, <video>
, <audio>
, and fullscreen-related strategies, however I don’t assume that something is for certain at this level.
So, what could be the advantages of invoker instructions?
Effectively, an entire lot much less JavaScript for one, particularly if extra invoker instructions are carried out over time. Moreover, we will hear for these instructions virtually as in the event that they had been JavaScript occasions. But when nothing else, invoker instructions merely present extra methods to work together with APIs such because the Dialog and Popover APIs. In a nutshell, it looks like numerous “dotting i’s” and “crossing-t’s” which isn’t a foul factor.