4 pontos por GN⁺ 2025-03-08 | 1 comentários | Compartilhar no WhatsApp
  • Botões são essenciais para criar aplicações web dinâmicas. Eles são usados para abrir menus, alternar ações e enviar formulários
  • No Chrome 135, os novos atributos command e commandfor melhoram e substituem os antigos atributos popovertargetaction e popovertarget
  • Problemas que surgiam ao implementar o comportamento de botões até agora:
    • O manipulador onclick do HTML pode ter uso limitado em código real por causa da política de segurança (CSP)
    • É necessário sincronizar o estado entre o botão e outros elementos, e o código para gerenciar esse estado mantendo a acessibilidade é complexo
    • Em React, AlpineJS, Svelte e outros, o gerenciamento de estado e de eventos também é complexo

Padrão command e commandfor

  • Com os atributos command e commandfor, um botão pode agir de forma declarativa sobre outro elemento. Isso oferece a conveniência dos frameworks, mantendo a flexibilidade
  • O botão com commandfor usa um ID (de forma parecida com o atributo for), e command aceita valores embutidos para uma abordagem mais intuitiva
  • Exemplo: implementação de um botão para abrir menu
    • Não é necessário aria-expanded nem JavaScript adicional
    <button commandfor="my-menu" command="show-popover">  
      Open Menu  
    </button>  
    <div popover id="my-menu">  
      <!-- ... -->  
    </div>  
    

command e commandfor vs popovertargetaction e popovertarget

  • Se você já usou popover, talvez conheça os atributos popovertarget e popovertargetaction
  • Eles funcionam de forma semelhante a commandfor e command, mas são específicos para popover
  • Os novos atributos substituem completamente os anteriores e oferecem funcionalidades adicionais

Comandos embutidos

  • O atributo command já traz embutidos comportamentos mapeados para várias APIs
    • show-popover: mapeado para el.showPopover()
    • hide-popover: mapeado para el.hidePopover()
    • toggle-popover: mapeado para el.togglePopover()
    • show-modal: mapeado para dialogEl.showModal()
    • close: mapeado para dialogEl.close()
  • Exemplo: implementação de um diálogo de confirmação de exclusão
    • É possível gerenciar estado e acessibilidade sem JavaScript
    <button commandfor="confirm-dialog" command="show-modal">  
      Delete Record  
    </button>  
    <dialog id="confirm-dialog">  
      <header>  
        <h1>Delete Record?</h1>  
        <button commandfor="confirm-dialog" command="close" aria-label="Close">  
          <img role="none" src="/close-icon.svg">  
        </button>  
      </header>  
      <p>Are you sure? This action cannot be undone</p>  
      <footer>  
        <button commandfor="confirm-dialog" command="close" value="cancel">  
          Cancel  
        </button>  
        <button commandfor="confirm-dialog" command="close" value="delete">  
          Delete  
        </button>  
      </footer>  
    </dialog>  
    
    • Código para tratar o resultado: é possível processar o valor de retorno no evento close do diálogo
    dialog.addEventListener("close", (event) => {  
      if (event.target.returnValue === "cancel") {  
        console.log("Cancel was clicked");  
      } else if (event.target.returnValue === "delete") {  
        console.log("Delete was clicked");  
      }  
    });  
    

Comandos personalizados

  • Além dos comandos embutidos, é possível definir comandos personalizados usando o prefixo --
  • Comandos personalizados disparam o evento "command" no elemento de destino, mas não executam lógica adicional
  • Exemplo: implementação de um comando para rotacionar imagem
    <button commandfor="the-image" command="--rotate-landscape">  
      Landscape  
    </button>  
    <button commandfor="the-image" command="--rotate-portrait">  
      Portrait  
    </button>  
    <img id="the-image" src="photo.jpg">  
    
    <script type="module">  
      const image = document.getElementById("the-image");  
      image.addEventListener("command", (event) => {  
        if (event.command === "--rotate-landscape") {  
          image.style.rotate = "-90deg";  
        } else if (event.command === "--rotate-portrait") {  
          image.style.rotate = "0deg";  
        }  
      });  
    </script>  
    

Tratamento de comandos no Shadow DOM

  • No Shadow DOM, como commandfor funciona com base em ID, existem as seguintes limitações:
    • Não é possível referenciar elementos entre Shadow DOMs diferentes
    • Nesse caso, é possível usar a API JavaScript para definir a propriedade .commandForElement
  • Exemplo: ligação de comando no Shadow DOM
    <my-element>  
      <template shadowrootmode="open">  
        <button command="show-popover">Show popover</button>  
        <slot></slot>  
      </template>  
      <div popover><!-- ... --></div>  
    </my-element>  
    
    <script>  
      customElements.define("my-element", class extends HTMLElement {  
        connectedCallback() {  
          const popover = this.querySelector('[popover]');  
          this.shadowRoot.querySelector('button').commandForElement = popover;  
        }  
      });  
    </script>  
    

Planos futuros

  • No Chrome, há planos para adicionar mais comandos embutidos:
    • abrir e fechar o elemento <details>
    • suporte ao comando show-picker em <input> e <select>
    • comandos de reprodução para <video> e <audio>
    • função de copiar texto de elementos

1 comentários

 
GN⁺ 2025-03-08
Comentários do Hacker News
  • Teóricos de linguagens de programação especulam desde os anos 80 sobre comefrom, uma versão mais poderosa de goto. Isso só foi implementado no INTERCAL. O INTERCAL é superior a linguagens como C em segurança, desempenho e ergonomia, mas tem enfrentado dificuldades para entrar no mercado comercial. É interessante ver o JavaScript incorporando esse recurso do INTERCAL. Espero que isso leve a um aumento da programação educada, assim como os objetos baseados em closures do JavaScript levaram a programação funcional ao mainstream

  • Invokers não são algo exclusivo do Chrome. Já estão disponíveis também no Firefox Nightly

  • A ideia de implementar comportamento declarativo de UI sem JS é atraente

    • Remove o boilerplate de popovers/modais (sem necessidade de manipular aria-expanded)
    • Comandos embutidos como show-modal integram acessibilidade à marcação
    • Comandos personalizados (ex.: --rotate-landscape) permitem que componentes exponham APIs via HTML
  • Dúvidas:

    • Abstração vs. mágica: isso só está movendo a complexidade do JS para o HTML? Frameworks já abstraem estado. Como isso vai coexistir?
    • Atrito com Shadow DOM: é preciso JS para definir .commandForElement entre shadow roots. Isso parece um problema resolvido só pela metade
    • Preparação para o futuro: se o OpenUI adicionar mais de 20 comandos (ex.: show-picker, toggle-details), a plataforma vai inchar com sintaxe de nicho?
  • Especificação:

    • elemento button, atributo commandfor
    • elemento button, atributo command
  • Isso é o padrão de ação/mensageria que Next, Be, Apple etc. usavam há cerca de 30 anos, ou estou deixando passar alguma coisa?

    • Isso era útil, mas evoluiu para um padrão de controlador baseado em interface por causa da complexidade de manter o padrão básico de design. Então, se essa caixa for aberta, espero muitos pedidos de melhoria
  • O antigo toolkit Java de UI da Netscape (IFC) permitia conectar elementos de ação

  • Os novos atributos command e commandfor melhoram e substituem popovertargetaction e popovertarget

    • Eles agora estão disponíveis por padrão? O que significa exatamente “substituem”? Isso será removido algum dia? Desenvolvedores web não podem simplesmente remover, em uma atualização, algo que deixou de ser necessário
  • Tenho alergia total a programar com strings. Entendo os benefícios de acessibilidade, mas não me anima particularmente usar IDs de elementos como mais uma camada de comportamento de app web

  • Não deveriam ter implementado isso sem a API completa. Em vez de uns 5 comandos, parece que seria possível implementar toda a funcionalidade de JavaScript via HTML. Isso daria milhares de comandos

  • Eu estava esperando algo sobre command and conquer em HTML

  • Melhorar e expandir o HTML é bom, mas ainda há um longo caminho pela frente. O pessoal do HTMX tem algumas boas ideias