2 points par GN⁺ 2025-05-05 | 1 commentaires | Partager sur WhatsApp
  • Le besoin d’éviter de répéter le même header sur plusieurs pages est fondamental, mais HTML ne dispose pas d’une balise include native pour le gérer directement
  • Les développeurs résolvent ce même problème via des solutions de contournement comme JavaScript fetch, des directives serveur, des générateurs de sites statiques, des langages de templates, des langages backend ou des Web Components
  • <iframe> est la méthode la plus proche d’un HTML pur, mais elle convient mal à cet usage en raison de problèmes de performance, d’accessibilité et d’utilisabilité, et reste globalement peu naturelle
  • CSS peut importer du CSS et JavaScript peut importer du JavaScript, mais HTML ne peut pas importer de HTML, ce qui donne l’impression de fragiliser la cohérence de la plateforme web
  • L’impact sur le preload scanner, les décalages de mise en page liés au chargement asynchrone, les includes imbriqués ou circulaires, l’augmentation des requêtes, les restrictions de domaine et une demande possiblement insuffisante restent des obstacles à la standardisation

Un besoin fondamental : réutiliser des fragments HTML répétés

  • Le problème apparaît lorsqu’il faut insérer le même header dans trois pages : index.html, about.html et contact.html
  • Au lieu de copier le même code trois fois, il est naturel de vouloir créer le header une seule fois puis l’inclure dans plusieurs pages
  • Quand le nombre de pages monte à des milliers, ce n’est plus seulement une question de confort, mais de prévention de la duplication de code

De nombreuses solutions existent déjà

  • Il est déjà possible de récupérer et d’insérer des fragments HTML via divers outils et couches de la pile
    • En JavaScript, on peut récupérer du HTML avec fetch puis l’insérer avec insertAdjacentElement
    • Il existe aussi les anciennes directives serveur Server Side Includes
    • On peut le gérer avec des fonctionnalités de générateurs de sites statiques comme Jekyll include
    • Des task runners comme gulp-include peuvent aussi être utilisés
    • Les langages de templates comme Handlebars partials proposent généralement une fonctionnalité d’include
    • Des langages backend peuvent aussi générer du HTML dynamiquement, comme include en PHP
    • Il existe également des approches fondées sur des Web Components dédiés à l’include
  • <iframe> est techniquement une façon de charger un autre document HTML avec du HTML seul, mais pour cet usage, ses problèmes de performance, d’accessibilité et d’utilisabilité sont importants
  • On peut aussi choisir de ne pas utiliser d’include du tout, en s’appuyant sur de puissantes fonctions de rechercher/remplacer

Et pourtant, rien dans HTML lui-même

  • Toutes les méthodes ci-dessus ne consistent pas à « récupérer du HTML avec une balise HTML et l’insérer à cet emplacement »
  • De la même manière que <img> récupère une image et l’insère là où elle est déclarée, HTML ne propose pas de balise déclarative directe du type « récupérer ce HTML et l’insérer ici »
  • La question a aussi été discutée dans ShopTalk Show, notamment avec Jake Archibald et Dave Rupert

Un décalage avec l’évolution habituelle de la plateforme web

  • Les standards du web et les navigateurs ont souvent tendance à intégrer à la plateforme des besoins que les développeurs résolvent déjà de manière répétée
  • Pour la gestion des dates, longtemps confiée à des bibliothèques JavaScript tierces, on a vu apparaître Temporal
  • Pour les transitions entre pages auparavant gérées par des frameworks, il y a désormais la View Transition API
  • Pour le positionnement sûr des éléments, domaine historiquement couvert par des bibliothèques, on voit arriver le CSS anchor positioning
  • Alors que presque tous les sites web ont besoin de réutiliser des fragments HTML et recourent chacun à des outils non standard différents, l’absence d’un include HTML ressemble à un vide assez inhabituel

Pourquoi un include HTML a du mal à devenir un standard

  • Du point de vue des navigateurs et de l’écosystème, un include HTML implique plusieurs contraintes
    • Il pourrait casser le preload scanner et nuire aux performances web
    • S’il devait être asynchrone, cela pourrait provoquer des secousses visuelles ou des sauts de mise en page pendant le chargement
    • Cela pourrait introduire une complexité qui nuit à la simplicité ou à la pureté de HTML
    • La gestion des includes imbriqués et des includes circulaires pourrait être difficile
    • Les hébergeurs web pourraient s’y opposer en raison de l’augmentation du nombre de requêtes
    • Contrairement aux images, au CSS ou au JavaScript, le HTML pourrait nécessiter des restrictions plus strictes lorsqu’il est chargé depuis un autre domaine
    • Il peut exister d’autres problèmes non listés ici
    • En pratique, la demande réelle pour cette fonctionnalité n’est peut-être pas si forte
  • Il s’agit moins d’apporter une réponse définitive que d’explorer les raisons possibles pour lesquelles HTML ne peut pas inclure directement du HTML

1 commentaires

 
GN⁺ 2025-05-05
Avis de Hacker News
  • Historiquement, HTML était une application de SGML, et SGML permettait les includes
    On pouvait définir une nouvelle « entity », et en créer une de type « system » permettait de la référencer plus tard pour qu’elle soit substituée. SGML était complexe, et plusieurs tentatives ont cherché à simplifier HTML ; cette fonctionnalité a alors été retirée au passage

    • Avec XHTML, on est brièvement passé du côté XML, et XML dispose de XInclude, mais ce n’est pas une fonctionnalité obligatoire
    • C’est une référence intéressante, je compte creuser davantage
      La balise en question semble inclure ou intégrer une autre page HTML. Page HTML intégrée : https://www.w3schools.com/tags/tag_object.asp
    • C’est une surface d’attaque complète à elle seule
      https://en.wikipedia.org/wiki/Billion_laughs_attack
    • C’était aussi présent dans les DTD utilisées dans HTML 4 et versions antérieures, ainsi qu’en XML, et cela vient probablement de SGML
  • C’est un terrier dans lequel je suis tombé à la fin des années 90, et dont je ne suis toujours pas sorti
    J’étais webmaster du site d’Analog Science Fiction, et créer des tonnes de pages statiques avec le même en-tête et la même barre latérale me rendait fou. En cherchant, j’ai découvert les includes côté serveur d’Apache, ce qui m’a permis de faire du DRY avant même de connaître le terme. Certains disent qu’un iframe suffit, mais ce n’est pas le cas. Un iframe ne s’agrandit pas pour s’adapter à la taille du contenu, et les solutions côté serveur nécessitent un serveur. Je ne vois pas pourquoi il ne pourrait pas exister une méthode simple côté client, et c’est une question qui mérite d’être envisagée maintenant qu’on corrige beaucoup d’irritants du développement web

    • Les includes côté serveur, c’était le top
      Quand j’ai commencé à fabriquer du « web stuff » avec un ami au milieu des années 90, le concept de DRY nous paraissait simplement naturel. À l’époque, notre FAI en accès commuté n’interdisait pas l’usage de .htaccess dans l’espace web utilisateur, ce qui permettait d’activer les includes côté serveur, et plus tard nous avons aussi trouvé comment activer CGI. J’ai même écrit en Perl un webshell rudimentaire pour explorer la machine du serveur web
    • C’est pour ce genre de raison que j’ai fini par aimer https://htmx.org
      C’est une petite bibliothèque de 10 Ko qui ajoute à HTML des fonctionnalités essentielles, comme l’import dynamique dans du HTML statique
    • La partie « les iframes ne s’agrandissent pas pour s’adapter au contenu » faisait en fait partie du plan initial
      https://caniuse.com/iframe-seamless
    • On pouvait déjà faire ce genre de chose avec Netscape en 1996. J’exploite encore un serveur de site web qui utilise cette méthode
      Ce qui m’a toujours agacé avec les frames, c’est qu’elles essayaient d’être trop intelligentes. Quand je faisais clic droit puis actualiser, je ne voulais pas recharger uniquement le HTML de la frame. Je comprends l’intention de mettre cela en cache séparément, mais les frames et le cache sont censés résoudre deux problèmes différents ; en les mélangeant, les deux sont devenus bancals. À mon avis, un include HTML devrait se comporter de la manière la plus bête possible. Il suffit de coller le texte inclus à l’endroit de l’include, et que le navigateur reçoive le texte résultant. Si l’on veut mettre en cache séparément la même navigation sur toutes les pages, on peut ajouter des attributs de cache et résoudre ce problème indépendamment. On peut toujours me convaincre qu’un include devrait faire davantage, mais ce comportement bête n’est pas un bug, c’est une fonctionnalité
    • La solution optimale consiste à générer des documents statiques avec un moteur de templates
  • Cette proposition de fonctionnalité s’appelait HTML Imports et a été créée dans le cadre des travaux sur les Web Components
    Elle était décrite ainsi : « HTML Imports are a way to include and reuse HTML documents in other HTML documents », et le document de planification se trouve sur https://www.w3.org/TR/html-imports/

    • Cela correspond à ce que le commentaire [1] de l’article mentionne : manque de demande, manque d’enthousiasme des fournisseurs, etc.
      Mais ces raisons sont presque des non-raisons qui n’expliquent rien en pratique. Cette fonctionnalité est demandée depuis 20 ans, et il existe toutes sortes d’implémentations en shim faites avec des scripts, des moteurs backend, etc. ; il est donc difficile de croire que la demande soit faible. Le refus des fournisseurs n’explique pas non plus pourquoi ils sont allés jusqu’à revenir sur des implémentations qui existaient déjà. L’argument des « implications de sécurité » est également étrange, puisqu’on peut déjà récupérer du HTML cross-origin avec une balise script et faire un document.write(). Pourquoi un script qui fait document.write() serait-il acceptable, alors qu’une balise HTML faisant la même chose deviendrait un gros problème ? Je comprends la préoccupation de sécurité visant à empêcher, par exemple, de cloner instantanément la page d’accueil de Google, mais cela semble facile à résoudre avec CORS
      [1] https://frontendmasters.com/blog/seeking-an-answer-why-cant-...
    • HTML Imports allait dans une direction similaire, mais ce n’est pas la fonctionnalité décrite dans le billet de blog
      Le HTML devrait être importé et affiché à un endroit précis du document, alors que HTML Imports ne permettait pas de le faire sans JavaScript. Pour plus de détails, voir https://github.com/whatwg/html/issues/2791#issuecomment-3112...
    • Pour être juste, c’était assez complexe
      De mémoire, après l’import il fallait instancier le template avec JavaScript ; ce n’était pas simplement une balise unique et toute simple
    • D’après https://caniuse.com/imports, Firefox l’avait aussi derrière un flag de configuration
    • HTML Imports était bien plus complexe que l’include demandé par cet article
  • Netscape 4 disposait de cette fonctionnalité sous forme d’inflow layer
    https://web.archive.org/web/19970630074729fw_/http://develop...
    https://web.archive.org/web/19970630094813fw_/http://develop...

    • D’après ce que je sais, changer l’attribut SRC avait tendance à provoquer pas mal de crashs, et la fonctionnalité a vite été retirée
      Je me souviens m’être amusé avec dans la bêta, mais elle avait disparu dans la version finale
    • Je me suis toujours demandé pourquoi ça s’appelait ILAYER, maintenant je comprends
  • Le nom de cette fonctionnalité est transclusion
    https://en.wikipedia.org/wiki/Transclusion
    Elle faisait partie du Project Xanadu et était à l’origine considérée comme une fonctionnalité importante de l’hypertexte. MediaWiki, en particulier, utilise abondamment la transclusion, et les wikis donnent parfois l’impression d’être la forme la plus pure de l’hypertexte

    • Ward Cunningham, le créateur du Wiki, a un temps essayé de créer un wiki orienté transclusion, où chacun aurait son propre espace wiki et utiliserait la transclusion de manière sociale
      https://en.wikipedia.org/wiki/Federated_Wiki
      Mais cela n’a jamais vraiment décollé
    • À mon avis, la vraie transclusion signifie bien plus que cela
      Dans Xanadu, on pouvait transclure dans un autre document seulement un extrait d’un document. Pour faire cela en HTML, il faut une réponse côté CSS. Dans certains cas, on peut déterminer et résoudre quelles propriétés doivent rester cohérentes entre le document hôte, le document invité et l’invité intégré dans l’hôte, mais dans le cas général ce n’est pas clair. Avec une simple approche par balise, le document invité devrait être conçu pour vivre dans l’environnement CSS que l’hôte lui impose. Une autre réponse simple est le Shadow DOM, qui permet généralement à l’invité d’appliquer ses propres styles sans affecter le reste du document. Même dans ce cas, je pense que l’hôte pourrait quand même ajouter quelques styles pour ajuster l’invité
  • J’ai l’impression que c’est ce que les vrais framesets essayaient de faire il y a longtemps. Je parle des framesets de l’époque HTML 4, pas des iframes
    Au moins, l’agrandissement automatique fonctionnait bien, et l’utilisateur pouvait aussi redimensionner à sa convenance. Les frames ont été beaucoup critiquées [1], mais elles ont été utilisées avec succès là où elles étaient utiles, comme dans la documentation de l’API Java [2]. Si elles ont fini par disparaître, c’est, à mon avis, parce qu’elles manquaient trop de souplesse pour les designers. C’était suffisant pour des pages d’information, mais les barres de défilement grossières et le découpage limité de l’écran ne répondaient pas aux attentes des designers. Aujourd’hui, les framesets tels quels ne fonctionneraient probablement pas bien sur mobile, il est donc trop tard pour les ressusciter
    [1] <https://www.nngroup.com/articles/why-frames-suck-most-of-the...> - Il est amusant de constater qu’une grande partie de ce contenu ne s’applique plus vraiment, et que tous les problèmes reprochés aux frames existent aujourd’hui sur le Web sous des formes encore plus désordonnées
    [2] <https://www.eeng.dcu.ie/~ee553/ee402notes/html/figures/JavaD...>

    • Le problème des framesets était bien plus fondamental
      Les liens profonds étaient impossibles, si bien que les personnes arrivant via un favori, Google ou des moteurs de recherche plus anciens tombaient sur une page sans navigation ; on essayait de contourner cela avec JavaScript, mais cela ne donnait pas une bonne expérience
  • La fonctionnalité « include » est considérée comme quelque chose traité côté serveur, c’est-à-dire en dehors du navigateur web
    HTML est côté client et, en réalité, ce n’est pas un langage de programmation mais une syntaxe de balisage. Comme le dit l’article, ce problème est déjà résolu. Ce que les étudiants en webdesign découvrent en premier en apprenant PHP, c’est include, et dans la plupart des CMS, include devient un partial de template expliqué dès le début de la documentation. Il n’y a pas vraiment besoin de rendre include possible avec HTML seul. HTML est un format de représentation et ne fait rien d’intéressant sans CSS ni JS

    • Dire que « include est une fonctionnalité côté serveur » n’est pas un argument pour dire qu’il ne devrait pas y avoir d’include côté client
      En fait, HTML possède déjà des versions pires avec les frames et les iframes. L’équivalent côté client du server-side include s’intègre naturellement à ce que les gens font avec HTML
    • Ce qui paraît étrange, c’est qu’un fichier HTML peut inclure des scripts, des polices, des images, des vidéos, des styles, etc., mais ne peut pas inclure du HTML
      On pourrait probablement le coder avec des éléments personnalisés, et je serais plutôt surpris s’il n’existait pas déjà un dépôt similaire sur GitHub
    • Il est vrai que c’est par là que beaucoup d’étudiants découvrent PHP. Mais je me demande quand même pourquoi une approche par simple balise n’est pas possible
      Il existe déjà du contenu chargé de manière asynchrone, comme les images ou le contenu en bas de page. Dire que « HTML n’est pas un langage de programmation mais une syntaxe de balisage » relève presque du flamebait : c’est un langage déclaratif interprété par différents moteurs de navigateur
    • Je suis d’accord avec ce qui a été dit, mais HTML n’est pas un format de représentation : c’est un langage de description de documents
      Si l’on parle de style, c’est CSS qui se charge de la présentation
    • Dire que « la fonctionnalité include est côté serveur » est juste. Un include côté serveur est tout à fait naturel, mais un include côté client signifie que le client doit pouvoir modifier le DOM d’origine à un moment qu’il ne connaît pas
      Il y a deux options. La première consiste à le traiter au moment du parsing HTML, donc avant la création du DOM, mais cela nécessite une requête synchrone au serveur pour l’include, ce qui n’est pas souhaitable. La seconde consiste, après la création du DOM, à faire apparaître un élément donné dans le DOM, à charger un fragment de manière asynchrone, puis à remplacer cet élément DOM par le fragment externe ; mais cela neutralise les mécanismes existants de validation de la structure du DOM. Cela dit, le moteur Sciter l’a implémenté avec la première stratégie. Le HTML de Sciter provient généralement de ressources locales de l’application ou du système de fichiers, si bien que le coût des requêtes de fragments supplémentaires est négligeable
      https://docs.sciter.com/docs/HTML/html-include
  • Comme d’autres l’ont dit, un include HTML pose plusieurs problèmes
    Si main.html inclut child/include1.html, et qu’à l’intérieur de child/include1.html il y a un lien src="include2.html", où l’utilisateur doit-il aller quand il clique ? S’il va vers include2.html, il s’agira probablement, d’après son nom, d’une page prévue pour l’include, donc il manquera le reste ; s’il va vers main.html, comment indiquer cette fois qu’il faut utiliser include2.html et non include1.html ? À l’inverse, article1.html, article2.html et article3.html pourraient chacun inclure header.html, footer.html et navi.html, mais alors, pour modifier globalement la structure de tous les articles, il faudrait modifier tous les articles. Si l’on veut ajouter comments.html à tous les articles, on finit par vouloir régénérer les pages avec un template, et à ce stade l’include dans le navigateur n’est plus nécessaire. Il y a aussi le problème du header qui doit connaître le titre, ou du footer qui doit connaître les liens précédent/suivant ; il faut alors un moyen d’échanger des informations entre les includes, et l’on revient finalement à la génération de pages. À bien y regarder, l’include HTML risque d’être pratiquement inutile dans la plupart des usages

    • Ces problèmes ont des solutions assez claires ; ils sont tous résolubles
      Deux cas d’usage différents sont mélangés ici. L’un est la réutilisation de fragments, l’autre une île autonome intégrable. Le second est déjà pris en charge par iframe, il ne reste donc qu’à résoudre le premier
    • Le raisonnement selon lequel include2.html perdrait le reste s’applique tout autant aux autres includes
      Si l’utilisateur clique sur un lien src="include.css", ce sera le bazar. Cela peut convenir à des données statiques, des images, du CSS ou du contenu HTML statique
  • Il existe une issue ouverte à ce sujet au WHATWG. Elle est aussi mentionnée dans la section commentaires de l’article de blog
    Client side include feature for HTML
    https://github.com/whatwg/html/issues/2791

  • HTML a eu des includes, puis ils ont perdu en popularité
    Le terme réel « include » désigne une fonctionnalité XML, et c’est aussi cette fonctionnalité que l’article souhaite. HTML avait une autre approche, antérieure à XML : les frames. Comme les frames faisaient beaucoup plus que les includes XML, HTML n’a pas obtenu cette fonctionnalité séparément. Les frames sont tombées en disgrâce pour plusieurs raisons : mauvais usages, sécurité, accessibilité, etc.

    • Contrairement aux framesets, il me semble que les includes XML n’ont jamais été correctement pris en charge par beaucoup de navigateurs, voire par aucun grand navigateur
      J’aime toujours les utiliser de temps en temps, mais il faut une étape de compilation qui les évalue avant de les transmettre à l’utilisateur ou au navigateur