Estoy aquí para compartir algo realmente útil que ha llegado recientemente a los navegadores: la API de Invoker Commands. Esta API proporciona una forma declarativa de controlar elementos interactivos con botones, reduciendo el código repetitivo de JavaScript para patrones de UI comunes como diálogos y popovers.
Históricamente, si querías que un botón abriera un diálogo modal o alternara un popover, recurrirías a JavaScript. Tomarías el botón, le añadirías un event listener, encontrarías el elemento objetivo y luego llamarías a su método showModal() o togglePopover(). Funcionaba, pero significaba esperar a que tu JavaScript se descargara y ejecutara, y aumentaba el tamaño de tu bundle. La plataforma web nos ofrece ahora una forma más declarativa de manejar esto, devolviendo esa responsabilidad al navegador.
¿Qué son los Invoker Commands?
La API de Invoker Commands introduce dos nuevos atributos HTML, commandfor y command, que aplicas directamente a un elemento <button>. Estos atributos permiten que un botón controle otros elementos interactivos en la página sin necesidad de JavaScript. Esto significa una interactividad inicial de la página mejorada, ya que el usuario no tiene que esperar a que JavaScript se descargue y ejecute para que estos botones sean interactivos.
Piensa en ello de esta manera:
commandfor: Este atributo convierte tu<button>en un "invocador de comandos" y le dice al navegador qué elemento debe controlar. Su valor es eliddel elemento objetivo.command: Este atributo especifica qué acción debe realizar el botón sobre el elementocommandfor.
Este enfoque declarativo puede complementar y, en muchos casos, reemplazar métodos más antiguos como popovertargetaction y popovertarget, ofreciendo una forma unificada y más potente de gestionar las interacciones de los botones.
Construcción práctica: Abrir diálogos y popovers
Vamos a construir un archivo HTML simple que demuestre cómo abrir tanto un popover como un diálogo modal usando solo estos nuevos atributos.
Implementación paso a paso
- Crea tu estructura HTML: Necesitaremos botones, un elemento
<dialog>y un elemento<div>con el atributopopover. - Vincula los botones a los objetivos: Usa
commandforpara apuntar alidde tu elemento objetivo. - Especifica las acciones: Usa
commandcon los valores integrados apropiados para popovers y diálogos.
Aquí tienes el código completo y funcional para un único archivo index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Invoker Commands: Dialogs & Popovers</title>
<style>
body {
font-family: sans-serif;
margin: 2rem;
display: flex;
flex-direction: column;
gap: 1rem;
align-items: flex-start;
}
dialog {
border: 1px solid #ccc;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
background-color: white;
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
[popover] {
border: 1px solid #ddd;
padding: 1rem;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
background-color: #f9f9f9;
}
[popover]:not(:popover-open) {
visibility: hidden; /* Hide popovers when not open */
}
</style>
</head>
<body>
<h1>Invoker Commands: Declarative UI</h1>
<h2>Popover Example</h2>
<button commandfor="myPopover" command="toggle-popover">
Toggle Popover
</button>
<div id="myPopover" popover>
<p>This is a popover controlled declaratively!</p>
<button commandfor="myPopover" command="hide-popover">
Close Popover
</button>
</div>
<h2>Modal Dialog Example</h2>
<button commandfor="myDialog" command="show-modal">
Show Modal Dialog
</button>
<dialog id="myDialog">
<h3>This is a Modal Dialog</h3>
<p>It blocks interaction with the rest of the page until dismissed.</p>
<p>Press ESC or click the button to close it.</p>
<button commandfor="myDialog" command="close">
Close Dialog
</button>
</dialog>
<h2>Custom Command Example</h2>
<button commandfor="my-img" command="--rotate-left">Rotate left</button>
<button commandfor="my-img" command="--rotate-right">Rotate right</button>
<img id="my-img" src="photo.jpg" alt="A sample image" style="transition: rotate 0.3s ease;"/>
<script>
// Custom Command JavaScript
const myImg = document.getElementById("my-img");
myImg.addEventListener("command", (event) => {
if (event.command === "--rotate-left") {
myImg.style.rotate = "-90deg";
console.log("Custom command: --rotate-left received!");
} else if (event.command === "--rotate-right") {
myImg.style.rotate = "90deg";
console.log("Custom command: --rotate-right received!");
}
});
// Basic feature detection (optional, for progressive enhancement)
if (!Object.hasOwn(HTMLButtonElement.prototype, "commandfor")) {
console.warn("Invoker Commands API not supported in this browser. Consider a polyfill or fallback JS.");
// Example fallback for a popover (you'd need to manually manage display/accessibility)
const fallbackPopoverButton = document.querySelector('button[commandfor="myPopover"]');
const fallbackPopover = document.getElementById('myPopover');
if (fallbackPopoverButton && fallbackPopover) {
fallbackPopoverButton.onclick = () => {
if (fallbackPopover.style.visibility === 'hidden' || fallbackPopover.style.visibility === '') {
fallbackPopover.style.visibility = 'visible';
fallbackPopover.style.opacity = '1';
} else {
fallbackPopover.style.visibility = 'hidden';
fallbackPopover.style.opacity = '0';
}
};
}
}
</script>
</body>
</html>
Explicación:
-
Popover:
- El
<div>conid="myPopover"también tiene el atributo globalpopover. Este atributo lo designa como un elemento popover, gestionando su visibilidad y comportamiento en la capa superior. - El botón "Toggle Popover" usa
commandfor="myPopover"ycommand="toggle-popover". Esto le dice al navegador que alterne la visibilidad del elemento conid="myPopover". - Dentro del popover, un botón "Close Popover" usa
commandfor="myPopover"ycommand="hide-popover"para cerrarlo explícitamente. - El atributo
popoverpor sí solo (sin valor) se establece por defecto enpopover="auto", lo que significa que puede ser "descartado ligeramente" (cerrado haciendo clic fuera o pulsando Escape), y normalmente solo se muestra un popover automático a la vez.
- El
-
Diálogo Modal:
- El elemento
<dialog>conid="myDialog"está diseñado inherentemente para un comportamiento modal. - El botón "Show Modal Dialog" usa
commandfor="myDialog"ycommand="show-modal". Esto abre el diálogo como modal, impidiendo la interacción con el resto de la página hasta que se cierra. - Dentro del diálogo, un botón "Close Dialog" usa
commandfor="myDialog"ycommand="close"para descartarlo. El navegador también se encarga de cerrar el diálogo cuando se pulsa la tecla Escape.
- El elemento
-
Comandos Personalizados:
- El atributo
commandno se limita a las acciones integradas. Puedes definir comandos personalizados prefijando sus nombres con--, de forma similar a las propiedades personalizadas de CSS. - El elemento
my-imgescucha un eventocommand. Este evento se dispara en el elemento referenciado porcommandfor. - El código JavaScript comprueba
event.commandpara determinar qué acción personalizada (--rotate-lefto--rotate-right) se activó y aplica el estilo correspondiente. Esto te permite crear APIs declarativas para tus componentes personalizados, reduciendo la necesidad de event listeners explícitos en los propios botones.
- El atributo
Advertencias y consideraciones
Compatibilidad con navegadores
La API de Invoker Commands, junto con los atributos commandfor y command, alcanzó el estado "Baseline" en diciembre de 2025. Esto significa que es compatible con todos los navegadores principales, incluyendo Chrome (135+), Edge (135+), Firefox (144+) y Safari (26.2+).
Sin embargo, si tu audiencia incluye usuarios con versiones de navegador más antiguas, necesitarás una solución alternativa.
Solución alternativa para navegadores antiguos
Para navegadores que no son compatibles con la API de Invoker Commands, normalmente recurrirías a los event listeners tradicionales de JavaScript. El código de ejemplo incluye una comprobación simple de detección de características usando Object.hasOwn(HTMLButtonElement.prototype, "commandfor") y una solución alternativa básica de JavaScript para el popover. Una solución más robusta implicaría un polyfill si necesitas un comportamiento consistente en todos los navegadores antiguos, o una estrategia de mejora progresiva más completa. Ten en cuenta que los polyfills para esta API podrían no replicar completamente el comportamiento nativo, especialmente en lo que respecta a los estados ARIA y la gestión del foco.
Permisos y HTTPS
La API de Invoker Commands en sí misma no introduce nuevas solicitudes de permisos ni requisitos estrictos de HTTPS más allá de lo estándar para las características de la plataforma web. Los diálogos y popovers se consideran generalmente elementos de UI seguros. Sin embargo, para cualquier sitio web en producción hoy en día, HTTPS es un requisito fundamental de seguridad y rendimiento, por lo que siempre deberías servir tu contenido a través de HTTPS, independientemente de si estás usando esta API.
Accesibilidad (A11y)
Una de las ventajas significativas de la API de Invoker Commands es que el navegador maneja gran parte de la accesibilidad automáticamente. Cuando usas commandfor y command con acciones integradas como show-modal o toggle-popover, el navegador gestiona el "focus trapping", la restauración del foco al cerrar, el manejo de la tecla Escape y los atributos ARIA necesarios (como aria-expanded). Esto simplifica enormemente la creación de componentes interactivos accesibles en comparación con escribir toda esa lógica manualmente en JavaScript.
Comandos personalizados y JavaScript
Aunque el beneficio principal es reducir JavaScript, la capacidad de definir comandos personalizados usando el manejador de eventos command significa que aún puedes aprovechar este patrón declarativo para interacciones más complejas donde el HTML puro no es suficiente. Esto proporciona una separación limpia: HTML declara qué comando enviar, y JavaScript escucha cómo ejecutarlo en el elemento objetivo.
Qué vigilar
- Adopción completa del ecosistema: Aunque los principales navegadores lo soportan, las herramientas y frameworks podrían tardar en integrar y promover completamente esta API.
- Nuevos comandos integrados: La especificación podría ampliarse para incluir más comandos integrados para otros elementos interactivos como
<details>o controles de medios. - Polyfills: Si necesitas soportar una gama muy amplia de navegadores antiguos, presta atención a la madurez y exhaustividad de los polyfills para esta API.