2 pontos por GN⁺ 2025-05-05 | 1 comentários | Compartilhar no WhatsApp
  • A necessidade de evitar repetir o mesmo header em várias páginas é básica, mas o HTML não tem uma tag nativa de include para lidar com isso diretamente
  • Desenvolvedores vêm resolvendo o mesmo problema com meios alternativos como JavaScript fetch, diretivas do servidor, geradores de sites estáticos, linguagens de template, linguagens de backend e Web Components
  • O <iframe> é o método mais próximo de HTML puro, mas não se encaixa bem nesse uso em termos de desempenho, acessibilidade e usabilidade, além de ser estranho no geral
  • O CSS pode importar CSS e o JavaScript pode importar JavaScript, mas o HTML não pode importar HTML, o que faz a consistência da plataforma web parecer abalada
  • Impacto no preload scanner, instabilidade de layout causada por carregamento assíncrono, includes aninhados e circulares, aumento de requisições, restrições de domínio e falta de demanda continuam sendo barreiras à padronização

A necessidade básica de reutilizar trechos repetidos de HTML

  • O problema aparece numa situação em que é preciso colocar o mesmo header em três páginas: index.html, about.html e contact.html
  • Em vez de copiar o mesmo código três vezes, é natural querer criar o header uma vez e incluí-lo em várias páginas
  • Quando o número de páginas cresce para milhares, isso deixa de ser mera conveniência e vira um problema de evitar duplicação de código

As várias soluções que já existem

  • Trazer e inserir trechos de HTML já é algo possível em várias ferramentas e camadas
    • Com JavaScript, é possível buscar HTML com fetch e inseri-lo com insertAdjacentElement
    • Também existem as antigas diretivas de servidor Server Side Includes
    • Dá para resolver com recursos de geradores de sites estáticos, como o Jekyll include
    • Também é possível usar task runners como o gulp-include
    • Linguagens de template como Handlebars partials normalmente oferecem recursos de include
    • Linguagens de backend também podem gerar HTML dinamicamente, como o include do PHP
    • Existe ainda a abordagem com Web Component voltado especificamente para include
  • O <iframe> é tecnicamente uma forma de trazer outro HTML usando só HTML puro, mas para esse uso ele traz grandes problemas de desempenho, acessibilidade e usabilidade
  • Também existe a escolha de simplesmente não usar include e confiar em recursos poderosos de localizar e substituir

Mas o próprio HTML não tem isso

  • Todos os métodos acima não funcionam como “um único elemento HTML que traz outro HTML e o coloca neste ponto”
  • Assim como o <img> traz uma imagem e a coloca naquele lugar, o HTML não tem uma tag declarativa direta que diga “traga este HTML e insira-o aqui”
  • Essa mesma pergunta já apareceu em discussões do ShopTalk Show com nomes como Jake Archibald e Dave Rupert

Onde isso destoa do fluxo normal da plataforma web

  • Padrões web e navegadores muitas vezes absorvem para a plataforma funcionalidades que desenvolvedores já vinham resolvendo repetidamente por conta própria
  • No fluxo em que se usava JavaScript de terceiros para lidar com datas, surgiu o Temporal
  • Para a necessidade de transições de página antes tratada por frameworks, existe a View Transition API
  • Para a área antes atendida por bibliotecas usadas para posicionar elementos com segurança, entrou o CSS anchor positioning
  • Considerando que praticamente todo site precisa reutilizar trechos de HTML e que cada um usa ferramentas não padronizadas diferentes, a ausência de HTML include parece uma lacuna incomum

Por que é difícil o HTML include virar padrão

  • Do ponto de vista dos navegadores e do ecossistema, HTML include traz vários custos
    • Pode quebrar o preload scanner e prejudicar o desempenho da web
    • Se tiver de ser assíncrono, pode causar tremores ou saltos visuais durante o carregamento
    • Pode introduzir uma complexidade que prejudica a simplicidade ou a pureza do HTML
    • Pode ser difícil lidar com includes aninhados e includes circulares
    • Empresas de hospedagem podem se opor por causa do aumento no número de requisições
    • Diferentemente de imagens, CSS e JavaScript, o HTML pode exigir restrições mais rígidas ao ser carregado de outros domínios
    • Pode haver outros problemas além dos listados
    • Na prática, a demanda por esse recurso talvez nem seja tão grande
  • Em vez de uma resposta definitiva, a discussão se aproxima mais de uma exploração das razões pelas quais o HTML não consegue incluir HTML diretamente

1 comentários

 
GN⁺ 2025-05-05
Opiniões no Hacker News
  • Historicamente, HTML era uma aplicação de SGML, e SGML permitia includes.
    Era possível definir uma nova “entity” e, ao criar uma entity “system”, ela podia ser referenciada depois para ser substituída. SGML era complexo, então houve várias tentativas de simplificar o HTML, e esse recurso acabou ficando de fora nesse processo.

    • Com XHTML, houve uma breve passagem pelo lado do XML, e XML tem XInclude, mas ele não é um recurso obrigatório.
    • Parece um material de referência interessante, então pretendo pesquisar mais.
      Essa tag parece incluir ou embutir outra página HTML. Página HTML embutida: https://www.w3schools.com/tags/tag_object.asp
    • Isso, por si só, é uma superfície de ataque completa.
      https://en.wikipedia.org/wiki/Billion_laughs_attack
    • Também existia no DTD usado no HTML 4 e anteriores e no XML, e provavelmente veio do SGML.
  • Foi uma toca de coelho em que caí no fim dos anos 90 e da qual ainda não consegui sair.
    Eu era webmaster do site da Analog Science Fiction, e estava enlouquecendo de tanto criar páginas estáticas com o mesmo cabeçalho e a mesma barra lateral. Pesquisando, descobri os server-side includes do Apache e consegui deixar tudo DRY antes mesmo de conhecer o termo DRY. Há quem diga que iframe basta, mas não basta. Iframe não se expande para acompanhar o tamanho do conteúdo, e uma solução do lado do servidor exige um servidor. Não sei por que não pode haver um método simples do lado do cliente, e é uma questão que vale considerar agora que muita coisa irritante do desenvolvimento web está sendo corrigida.

    • Server-side include era o máximo.
      Quando comecei a fazer “web stuff” com um amigo em meados dos anos 90, o conceito de DRY simplesmente parecia natural. O ISP discado da época não bloqueava o uso de .htaccess no espaço web dos usuários, então dava para habilitar server-side include; mais tarde descobri até como habilitar CGI. Cheguei a escrever um web shell rudimentar em Perl para explorar a máquina do servidor web.
    • É por isso que passei a gostar do https://htmx.org.
      É uma biblioteca pequena de 10 KB que reforça o HTML com funcionalidades essenciais, como import dinâmico em HTML estático.
    • A parte de que “iframe não se expande para acompanhar o conteúdo” na verdade estava nos planos originais.
      https://caniuse.com/iframe-seamless
    • Isso já era possível no Netscape em 1996. Ainda mantenho um servidor de site que usa esse método.
      O que sempre me incomodou nos frames era que eles tentavam ser espertos demais. Quando eu clicava com o botão direito e recarregava, não queria que só o HTML do frame fosse recarregado. Entendo a intenção de fazer cache separadamente, mas frames e cache deveriam resolver problemas diferentes; ao misturar os dois, ambos ficaram meio ambíguos. Acho que um include em HTML deveria se comportar da forma mais burra possível. Bastaria colar o texto incluído no ponto do include, e o navegador receberia esse texto resultante. Se você quiser fazer cache da mesma navegação em todas as páginas separadamente, adicione atributos de cache e resolva esse problema de forma independente. Dá para argumentar que include deveria fazer mais coisas, mas o comportamento burro não é bug, é feature.
    • A solução ideal é gerar documentos estáticos com um template engine.
  • A proposta desse recurso se chamava HTML Imports e foi criada como parte do trabalho em Web Components.
    Havia a descrição “HTML Imports are a way to include and reuse HTML documents in other HTML documents”, e o documento de planejamento está em https://www.w3.org/TR/html-imports/.

    • Isso bate com o que o comentário [1] do artigo menciona, como falta de demanda e falta de entusiasmo dos fornecedores.
      Mas esses motivos são quase não motivos, porque na prática não explicam nada. Esse recurso vem sendo pedido há 20 anos, e há todo tipo de implementação shim feita com scripts, motores de backend etc.; por isso é difícil acreditar que a demanda seja baixa. A rejeição dos fornecedores também não explica por que ela foi forte a ponto de reverter uma implementação que já existia. “Impactos de segurança” também soa estranho, porque já é possível buscar HTML de outra origem com uma tag script e fazer document.write(). Fico curioso por que um script fazendo document.write() é aceitável, enquanto uma tag HTML que faz a mesma coisa vira um grande problema. Entendo a preocupação de segurança de impedir algo como clonar a página inicial do Google na hora, mas isso parece facilmente resolvível com CORS.
      [1] https://frontendmasters.com/blog/seeking-an-answer-why-cant-...
    • HTML Imports também seguia uma direção parecida, mas não é o mesmo recurso que o post do blog descreve.
      O HTML deveria ser importado e exibido em uma posição específica do documento, mas HTML Imports não conseguia fazer isso sem JavaScript. Para mais detalhes, veja https://github.com/whatwg/html/issues/2791#issuecomment-3112....
    • Para ser justo, era bem complexo.
      Pelo que lembro, depois de importar era preciso instanciar o template com JavaScript; não era algo feito com uma única tag simples.
    • Em https://caniuse.com/imports, dá para ver que o Firefox também tinha isso por trás de uma flag de configuração.
    • HTML Imports era muito mais complexo do que o include que este artigo está pedindo.
  • O Netscape 4 tinha esse recurso como inflow layer
    https://web.archive.org/web/19970630074729fw_/http://develop...
    https://web.archive.org/web/19970630094813fw_/http://develop...

    • Pelo que sei, a ação de alterar o atributo SRC era bastante propensa a crashes, e o recurso também foi removido logo depois
      Lembro de ter brincado com isso na beta, mas na versão final ele já tinha desaparecido
    • Sempre me perguntei por que o nome era ILAYER, e agora entendi
  • O nome desse recurso é transclusão (transclusion)
    https://en.wikipedia.org/wiki/Transclusion
    Fazia parte do Project Xanadu e originalmente era considerado um recurso importante do hipertexto. Em especial, o MediaWiki usa transclusão extensivamente, e às vezes wikis parecem a forma mais pura de hipertexto

    • Ward Cunningham, criador do Wiki, certa vez tentou criar uma wiki centrada em transclusão, em que todos teriam seu próprio espaço wiki e usariam transclusion socialmente
      https://en.wikipedia.org/wiki/Federated_Wiki
      Mas ela nunca decolou de verdade
    • Acho que transclusion de verdade significa ainda mais do que isso
      No Xanadu, era possível fazer transclude apenas de um trecho de um documento em outro documento. Para fazer isso em HTML, é preciso ter uma resposta para CSS. Em certos cenários, dá para decidir e resolver quais propriedades devem permanecer consistentes entre o documento host, o documento convidado e o convidado embutido dentro do host, mas no caso geral isso é pouco claro. Se fosse uma abordagem simples baseada em tags, o documento convidado teria de ser projetado para viver dentro do ambiente de CSS que o host colocou. Outra resposta simples é o Shadow DOM, que em geral permite que o convidado aplique seu próprio estilo sem afetar o restante do documento. Mesmo nesse caso, acho que o host ainda poderia inserir alguns estilos para ajustar o convidado
  • Acho que era isso que os framesets de verdade tentavam fazer muito tempo atrás. Não iframe, mas frameset da época do HTML 4
    Pelo menos a expansão automática de tamanho funcionava bem, e o usuário também podia ajustar para o tamanho que quisesse. Havia muitas críticas a frames [1], mas eles foram usados com sucesso em lugares úteis, como a documentação da Java API [2]. Acho que acabaram desaparecendo porque eram flexíveis demais pouco para designers. Eram suficientes para páginas informativas, mas barras de rolagem grosseiras e a divisão limitada da tela não atendiam às demandas dos designers. Hoje, framesets do jeito que eram provavelmente não funcionariam bem no mobile, então é tarde demais para ressuscitá-los
    [1] <https://www.nngroup.com/articles/why-frames-suck-most-of-the...> - É curioso que boa parte do conteúdo dali já não se aplique, e que todos os problemas apontados nos frames existam na Web atual em formas ainda mais bagunçadas
    [2] <https://www.eeng.dcu.ie/~ee553/ee402notes/html/figures/JavaD...>

    • O problema dos framesets era muito mais fundamental
      Como deep links eram impossíveis, quem chegava por um bookmark, pelo Google ou por mecanismos de busca anteriores caía em uma página sem navegação; tentaram contornar isso com JavaScript, mas não conseguiam criar uma boa experiência
  • O recurso de “include” é considerado algo processado no lado do servidor, ou seja, fora do navegador web
    HTML é do lado do cliente e, na prática, não é uma linguagem de programação, mas uma sintaxe de marcação. Como o texto diz, esse problema já foi resolvido. A primeira coisa que estudantes de web design encontram ao aprender PHP é include, e na maioria dos CMSs o include vira um partial de template e é explicado logo no começo da documentação. Não há muita necessidade de tornar include possível apenas com HTML. HTML é um formato de apresentação e não faz nada interessante sem CSS e JS

    • Dizer que “include é um recurso do lado do servidor” não é argumento para dizer que não deve haver include do lado do cliente
      Na prática, o HTML já tem versões piores disso: frames e iframes. O equivalente do lado do cliente ao server-side include se encaixa naturalmente no que as pessoas fazem com HTML
    • O motivo de parecer estranho é que um arquivo HTML consegue incluir scripts, fontes, imagens, vídeos, estilos etc., mas não consegue incluir HTML
      Provavelmente daria para codificar isso com elementos customizados, e eu ficaria até surpreso se não houvesse um repositório parecido no GitHub
    • É verdade que isso é a porta de entrada de muitos estudantes para PHP. Ainda assim, fico curioso por que não pode haver uma abordagem simples baseada em tag
      Já existe conteúdo carregado de forma assíncrona, como imagens ou conteúdo mais abaixo na tela. Dizer que “HTML não é uma linguagem de programação, mas uma sintaxe de marcação” beira o flamebait; é uma linguagem declarativa interpretada por cada engine de navegador
    • Concordo com o que foi dito, mas HTML não é um formato de apresentação, e sim uma linguagem de descrição de documentos
      Se a questão é estilização, a apresentação fica a cargo do CSS
    • A afirmação de que “include é um recurso do lado do servidor” está correta. Include no lado do servidor é totalmente natural, mas include no lado do cliente significa que o cliente teria de conseguir modificar o DOM original em um momento que não pode conhecer
      Há duas opções. A primeira é fazer isso no momento do parsing do HTML, isto é, antes da criação do DOM, mas isso exigiria uma requisição síncrona ao servidor para o include, o que não é desejável. A segunda é, depois da criação do DOM, fazer um determinado elemento aparecer no DOM, carregar o fragmento de forma assíncrona e então substituir esse elemento do DOM pelo fragmento externo, mas isso neutraliza o mecanismo existente de validação da estrutura do DOM. No entanto, no engine Sciter isso foi implementado pela primeira estratégia. Como o HTML do Sciter normalmente vem de recursos locais da aplicação ou do sistema de arquivos, o custo de requisitar fragmentos adicionais é desprezível
      https://docs.sciter.com/docs/HTML/html-include
  • Como outras pessoas disseram, um include em HTML tem vários problemas
    Se main.html inclui child/include1.html, e dentro de child/include1.html há um link src="include2.html", para onde o usuário deve ir ao clicar? Se for para include2.html, pelo nome provavelmente é uma página feita para include e o restante estará faltando; se for para main.html, como especificar que desta vez deve ser usado include2.html em vez de include1.html? Por outro lado, também seria possível fazer article1.html, article2.html e article3.html incluírem cada um header.html, footer.html e navi.html, mas então, para alterar globalmente a estrutura de todos os artigos, seria preciso modificar todos eles. Se você quiser adicionar comments.html a todos os artigos, no fim vai querer gerar as páginas novamente com templates, e nesse ponto o include no navegador deixa de ser necessário. Também surgem problemas como o cabeçalho precisar saber o título ou o rodapé precisar saber os links anterior/próximo, e seria necessário um modo de trocar informações entre includes, o que acaba levando de volta à geração de páginas. Pensando bem, é bem provável que include em HTML seja praticamente inútil para a maioria dos usos

    • Esses problemas são todos solucionáveis, com soluções bastante claras
      Há dois casos de uso diferentes misturados aqui. Um é a reutilização de fragmentos, e o outro é uma ilha independente incorporável. O segundo já é tratado por iframe, então basta resolver o primeiro
    • A lógica de que include2.html perde o restante se aplica da mesma forma a outros includes
      Se o usuário clicar em um link src="include.css", vai virar uma bagunça. Pode ser aceitável para dados estáticos, imagens, CSS e conteúdo HTML estático
  • Há uma issue aberta relacionada a isso no WHATWG. Ela também é mencionada na seção de comentários do post do blog
    Client side include feature for HTML
    https://github.com/whatwg/html/issues/2791

  • HTML já teve include, e ele perdeu popularidade
    O termo real “include” é um recurso de XML, e também é esse recurso que o texto está querendo. Antes do XML, o HTML tinha outra abordagem, que eram os frames. Como frames faziam muito mais coisas do que XML include, o HTML não recebeu esse recurso separadamente. Frames perderam popularidade por vários problemas, como mau uso, segurança e acessibilidade

    • Ao contrário de frameset, acho que XML include nunca foi devidamente suportado por muitos navegadores — talvez por nenhum dos principais
      Ainda gosto de usá-lo ocasionalmente, mas é necessária uma etapa de compilação que o avalie antes de entregá-lo ao usuário ou ao navegador