Volver a las notas

Invoker Commands API: control declarativo de la interfaz

17 de octubre de 2026·
htmlwebdevinvokercommandspopover

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 el id del elemento objetivo.
  • command: Este atributo especifica qué acción debe realizar el botón sobre el elemento commandfor.

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

  1. Crea tu estructura HTML: Necesitaremos botones, un elemento <dialog> y un elemento <div> con el atributo popover.
  2. Vincula los botones a los objetivos: Usa commandfor para apuntar al id de tu elemento objetivo.
  3. Especifica las acciones: Usa command con 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> con id="myPopover" también tiene el atributo global popover. 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" y command="toggle-popover". Esto le dice al navegador que alterne la visibilidad del elemento con id="myPopover".
    • Dentro del popover, un botón "Close Popover" usa commandfor="myPopover" y command="hide-popover" para cerrarlo explícitamente.
    • El atributo popover por sí solo (sin valor) se establece por defecto en popover="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.
  • Diálogo Modal:

    • El elemento <dialog> con id="myDialog" está diseñado inherentemente para un comportamiento modal.
    • El botón "Show Modal Dialog" usa commandfor="myDialog" y command="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" y command="close" para descartarlo. El navegador también se encarga de cerrar el diálogo cuando se pulsa la tecla Escape.
  • Comandos Personalizados:

    • El atributo command no 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-img escucha un evento command. Este evento se dispara en el elemento referenciado por commandfor.
    • El código JavaScript comprueba event.command para determinar qué acción personalizada (--rotate-left o --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.

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.

Fuentes