Por que o HTML sozinho não consegue oferecer suporte a `include`?
(frontendmasters.com)- 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.htmlecontact.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
fetche inseri-lo cominsertAdjacentElement - 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
includedo PHP - Existe ainda a abordagem com Web Component voltado especificamente para include
- Com JavaScript, é possível buscar HTML com
- 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
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.
Essa tag parece incluir ou embutir outra página HTML. Página HTML embutida: https://www.w3schools.com/tags/tag_object.asp
https://en.wikipedia.org/wiki/Billion_laughs_attack
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.
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
.htaccessno 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.É uma biblioteca pequena de 10 KB que reforça o HTML com funcionalidades essenciais, como import dinâmico em HTML estático.
https://caniuse.com/iframe-seamless
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 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/.
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 fazendodocument.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-...
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....
Pelo que lembro, depois de importar era preciso instanciar o template com JavaScript; não era algo feito com uma única tag simples.
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...
SRCera bastante propensa a crashes, e o recurso também foi removido logo depoisLembro de ter brincado com isso na beta, mas na versão final ele já tinha desaparecido
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
https://en.wikipedia.org/wiki/Federated_Wiki
Mas ela nunca decolou de verdade
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...>
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 JSNa 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
Provavelmente daria para codificar isso com elementos customizados, e eu ficaria até surpreso se não houvesse um repositório parecido no GitHub
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
Se a questão é estilização, a apresentação fica a cargo do CSS
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.htmlincluichild/include1.html, e dentro dechild/include1.htmlhá um linksrc="include2.html", para onde o usuário deve ir ao clicar? Se for parainclude2.html, pelo nome provavelmente é uma página feita para include e o restante estará faltando; se for paramain.html, como especificar que desta vez deve ser usadoinclude2.htmlem vez deinclude1.html? Por outro lado, também seria possível fazerarticle1.html,article2.htmlearticle3.htmlincluírem cada umheader.html,footer.htmlenavi.html, mas então, para alterar globalmente a estrutura de todos os artigos, seria preciso modificar todos eles. Se você quiser adicionarcomments.htmla 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 usosHá 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
include2.htmlperde o restante se aplica da mesma forma a outros includesSe 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áticoHá 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
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