2 puntos por GN⁺ 2025-05-05 | 1 comentarios | Compartir por WhatsApp
  • La necesidad de no repetir el mismo header en varias páginas es básica, pero HTML no tiene una etiqueta nativa de include para manejarlo directamente
  • Los desarrolladores han resuelto el mismo problema con vías alternativas como JavaScript fetch, directivas del servidor, generadores de sitios estáticos, lenguajes de plantillas, lenguajes backend y Web Components
  • <iframe> es el método más cercano a HTML puro, pero no encaja bien para este uso por temas de rendimiento, accesibilidad y usabilidad, y en general resulta incómodo
  • CSS puede importar CSS y JavaScript puede importar JavaScript, pero HTML no puede importar HTML, lo que hace que parezca romperse la consistencia de la plataforma web
  • El impacto en el preload scanner, los saltos de layout por carga asíncrona, los includes anidados o circulares, el aumento de solicitudes, las restricciones de dominio y la posible falta de demanda siguen siendo barreras para su estandarización

La necesidad básica de reutilizar fragmentos repetidos de HTML

  • El problema se hace evidente cuando hay que poner el mismo header en tres páginas: index.html, about.html y contact.html
  • En vez de copiar el mismo código tres veces, lo natural es querer crear el header una sola vez e incluirlo en varias páginas
  • Si el sitio crece a miles de páginas, ya no se trata solo de comodidad, sino de evitar la duplicación de código

Las distintas soluciones que ya existen

  • Traer e insertar fragmentos de HTML ya es posible en varias herramientas y capas
  • <iframe> es técnicamente una forma de traer otro HTML usando solo HTML, pero para este caso tiene grandes problemas de rendimiento, accesibilidad y usabilidad
  • También existe la opción de no usar includes y confiar en una buena función de buscar y reemplazar

Pero en HTML en sí no existe

  • Todos los métodos anteriores no son realmente una forma de “traer este HTML e insertarlo aquí con una sola etiqueta HTML”
  • Así como <img> trae una imagen y la coloca en ese lugar, HTML no tiene una etiqueta declarativa directa que diga “trae este HTML y ponlo aquí”
  • En ShopTalk Show también se discutió esta misma pregunta con Jake Archibald y Dave Rupert, entre otros

Un punto que choca con la dirección que ya ha tomado la plataforma web

  • Los estándares web y los navegadores muchas veces terminan absorbiendo como funciones nativas cosas que los desarrolladores ya resolvían repetidamente por su cuenta
  • En el caso del manejo de fechas, donde antes se usaba JavaScript de terceros, apareció Temporal
  • Para la necesidad de transiciones entre páginas que antes resolvían los frameworks, surgió la View Transition API
  • En el terreno de las librerías usadas para posicionar elementos de forma segura, llegó CSS anchor positioning
  • Casi todos los sitios web necesitan reutilizar fragmentos de HTML y cada uno lo resuelve con herramientas no estándar distintas, así que la ausencia de HTML include parece un vacío inusual

Por qué es difícil que HTML include se vuelva un estándar

  • Desde la perspectiva del navegador y del ecosistema, HTML include trae varias cargas
    • Puede romper el preload scanner y afectar negativamente el rendimiento web
    • Si tuviera que ser asíncrono, podría generar una experiencia con saltos o parpadeos en la interfaz mientras carga
    • Podría introducir una complejidad que afecta la simplicidad o pureza de HTML
    • Los includes anidados y los includes circulares podrían ser difíciles de manejar
    • Los proveedores de hosting web podrían oponerse por el aumento en la cantidad de solicitudes
    • A diferencia de imágenes, CSS o JavaScript, HTML podría requerir restricciones más estrictas al cargarse desde otros dominios
    • Puede haber otros problemas no incluidos en esta lista
    • En la práctica, quizá la demanda real por esta función no sea tan grande
  • Más que una respuesta definitiva, se trata de una exploración de razones sobre por qué HTML no puede incluir HTML directamente

1 comentarios

 
GN⁺ 2025-05-05
Opiniones de Hacker News
  • Históricamente, HTML era una aplicación de SGML, y SGML sí permitía includes.
    Se podían definir nuevas “entities”, y si se creaba una entity “system”, luego se podía referenciar para que fuera sustituida. SGML era complejo, así que hubo varios intentos de simplificar HTML, y en ese proceso también se eliminó esta función.

    • Con XHTML se pasó brevemente por el lado de XML, y XML tiene XInclude, pero no es una función obligatoria.
    • Es una referencia interesante, así que pienso investigar más.
      Esa etiqueta parece incluir o embeber otra página HTML. Página HTML embebida: https://www.w3schools.com/tags/tag_object.asp
    • Eso, por sí solo, es toda una superficie de ataque.
      https://en.wikipedia.org/wiki/Billion_laughs_attack
    • También existía en los DTD usados en HTML 4 o anterior y en XML, y probablemente venía de SGML.
  • Fue una madriguera en la que caí a fines de los 90 y de la que todavía no he salido.
    Era webmaster del sitio web de Analog Science Fiction, y estaba volviéndome loco creando montones de páginas estáticas con el mismo header y la misma barra lateral. Investigando, descubrí los server-side includes de Apache, y pude hacer las cosas de forma DRY incluso antes de conocer el término DRY. Algunos dicen que con iframe basta, pero no basta. Un iframe no se expande para ajustarse al tamaño del contenido, y las soluciones del lado del servidor requieren un servidor. No sé por qué no debería existir un método simple del lado del cliente, y ahora que se están corrigiendo muchas molestias del desarrollo web, es una pregunta que vale la pena considerar.

    • Los server-side includes eran lo máximo.
      Cuando a mediados de los 90 empecé a hacer “web stuff” con un amigo, el concepto de DRY simplemente se entendía de forma natural. En esa época, nuestro ISP de dial-up no bloqueaba el uso de .htaccess en el espacio web de los usuarios, así que pudimos activar server-side includes, y más tarde descubrimos cómo activar también CGI. Incluso escribí una web shell rudimentaria en Perl para explorar la máquina del servidor web.
    • Por eso me gusta https://htmx.org.
      Es una pequeña biblioteca de 10 KB que le agrega a HTML funciones esenciales como imports dinámicos de HTML estático.
    • La parte de que “un iframe no se expande para ajustarse al contenido” en realidad estaba en el plan original.
      https://caniuse.com/iframe-seamless
    • En Netscape de 1996 también se podía hacer algo así. Todavía opero un servidor de un sitio web que usa este método.
      Lo que siempre me molestó de los frames es que intentaban ser demasiado inteligentes. Cuando hacía clic derecho y recargaba, no quería que solo se volviera a cargar el HTML del frame. Entiendo la intención de cachearlo por separado, pero los frames y la caché deberían resolver problemas distintos, y al mezclarlos dejaron ambos a medias. Creo que un include de HTML debería comportarse de la forma más tonta posible. Basta con pegar el texto incluido en la ubicación del include y que el navegador reciba el texto resultante. Si quieres cachear por separado la misma navegación en todas las páginas, se puede agregar un atributo de caché y resolver ese problema de manera independiente. Se podría convencerme de que un include debería hacer más cosas, pero el comportamiento tonto no es un bug, es una función.
    • La solución óptima es generar documentos estáticos con un motor de plantillas.
  • Esta propuesta de función se llamaba HTML Imports y se creó como parte del trabajo en Web Components.
    Se describía como “HTML Imports are a way to include and reuse HTML documents in other HTML documents”, y el documento del plan está en https://www.w3.org/TR/html-imports/.

    • Coincide con lo mencionado en el comentario [1] del artículo: falta de demanda, poco entusiasmo de los vendors, etc.
      Pero esas razones se acercan más a no-razones que en realidad no explican nada. Esta función se ha pedido durante 20 años, y hay todo tipo de implementaciones shim hechas con scripts, motores de backend, etc.; cuesta creer que la demanda sea baja. El rechazo de los vendors tampoco explica por qué fue tan fuerte como para revertir implementaciones que ya existían. Lo de las “implicaciones de seguridad” también es raro, porque ya se puede traer HTML de otro origen con una etiqueta script y hacer document.write(). Me pregunto por qué está bien que un script haga document.write(), pero que una etiqueta HTML haga lo mismo sería un gran problema. Entiendo la preocupación de seguridad de impedir cosas como clonar al vuelo la página principal de Google, pero eso parece resolverse fácilmente con CORS.
      [1] https://frontendmasters.com/blog/seeking-an-answer-why-cant-...
    • HTML Imports iba en una dirección parecida, pero no era lo mismo que la función de la que habla el post del blog.
      El HTML debería importarse y mostrarse en una ubicación específica del documento, pero HTML Imports no podía hacer eso sin JavaScript. Para más detalles, ver https://github.com/whatwg/html/issues/2791#issuecomment-3112....
    • Para ser justos, era bastante complejo.
      Según recuerdo, después de importar había que instanciar la plantilla con JavaScript, y no era simplemente una sola etiqueta sencilla.
    • Según https://caniuse.com/imports, Firefox también lo tenía mediante un flag de configuración.
    • HTML Imports era mucho más complejo que el include que pide este artículo.
  • Netscape 4 tenía esta función como inflow layer
    https://web.archive.org/web/19970630074729fw_/http://develop...
    https://web.archive.org/web/19970630094813fw_/http://develop...

    • Por lo que sé, cambiar el atributo SRC era bastante propenso a crashear, y la función se eliminó poco después
      Recuerdo haber jugado con eso en la beta, pero en la versión final ya había desaparecido
    • Siempre me pregunté por qué se llamaba ILAYER, y ahora lo entiendo
  • El nombre de esta función es transclusión (transclusion)
    https://en.wikipedia.org/wiki/Transclusion
    Formaba parte de Project Xanadu y originalmente se consideraba una función importante del hipertexto. En particular, MediaWiki usa la transclusión de forma amplia y, a veces, una wiki se siente como la forma más pura del hipertexto

    • Ward Cunningham, creador de Wiki, alguna vez intentó crear una wiki centrada en la transclusión, donde todos tuvieran su propio espacio wiki y usaran la transclusion de manera social
      https://en.wikipedia.org/wiki/Federated_Wiki
      Pero no logró despegar realmente
    • Creo que la verdadera transclusion significa mucho más que eso
      En Xanadu se podía transcluir en otro documento solo un extracto parcial de un documento. Para hacer esto en HTML, hace falta una respuesta sobre CSS. En ciertos casos se puede resolver decidiendo qué propiedades mantener consistentes entre el documento anfitrión, el documento invitado y el invitado incrustado dentro del anfitrión, pero en el caso general no está claro. Si fuera un enfoque simple basado en etiquetas, el documento invitado tendría que estar diseñado para vivir dentro del entorno CSS que el anfitrión le haya puesto. Otra respuesta simple es Shadow DOM, que en general permite que el invitado aplique sus propios estilos sin afectar al resto del documento. Incluso en este caso, creo que el anfitrión podría agregar algunos estilos para ajustar al invitado
  • Creo que esto era lo que hace mucho intentaba hacer un frameset bien implementado. No me refiero a iframe, sino al frameset de la época de HTML 4
    Al menos la expansión automática de tamaño funcionaba bien y el usuario también podía ajustarlo al tamaño que quisiera. Hubo muchas críticas a los frames [1], pero se usaron con éxito en lugares donde eran útiles, como la documentación de la API de Java [2]. Creo que al final desaparecieron porque les faltaba demasiada flexibilidad para los diseñadores. Para páginas de información eran suficientes, pero las barras de desplazamiento toscas y la división limitada de la pantalla no satisfacían las necesidades de los diseñadores. Hoy, un frameset tal cual probablemente no funcionaría bien en móvil, así que ya es demasiado tarde para revivirlo
    [1] <https://www.nngroup.com/articles/why-frames-suck-most-of-the...> - Es interesante que mucho de lo que dice ahí ya no aplica, y que todos los problemas señalados en los frames existen en la web actual de formas más desordenadas
    [2] <https://www.eeng.dcu.ie/~ee553/ee402notes/html/figures/JavaD...>

    • El problema de frameset era mucho más fundamental
      Como no permitía deep linking, quienes llegaban por un marcador, por Google o por buscadores anteriores caían en una página sin navegación, y aunque se intentaba esquivarlo con JavaScript, no daba una buena experiencia
  • Se considera que la función “include” se procesa del lado del servidor, es decir, fuera del navegador web
    HTML es del lado del cliente y, en realidad, no es un lenguaje de programación sino una sintaxis de marcado. Como dice el artículo, este problema ya está resuelto. Lo primero que suelen ver los estudiantes de diseño web al aprender PHP es include, y en la mayoría de los CMS include se convierte en un partial de plantilla y se explica al comienzo de la documentación. No hay una necesidad particular de hacer posible include solo con HTML. HTML es un formato de presentación y, sin CSS y JS, no hace cosas interesantes

    • Decir que “include es una función del lado del servidor” no es un argumento para que no pueda existir un include del lado del cliente
      De hecho, HTML ya tiene versiones peores: frames e iframes. La contraparte del lado del cliente de un include del lado del servidor encaja de manera natural con lo que la gente hace con HTML
    • La razón por la que se siente raro es que un archivo HTML puede incluir scripts, fuentes, imágenes, video, estilos, etc., pero no puede incluir HTML
      Probablemente se podría programar con elementos personalizados, y me sorprendería que no hubiera un repositorio similar en GitHub
    • Es cierto que para muchos estudiantes es la puerta de entrada a PHP. Aun así, da curiosidad por qué no puede hacerse con una simple etiqueta
      También hay contenido que ya se carga de forma asíncrona, como imágenes o contenido en la parte inferior de la pantalla. Decir que “HTML no es un lenguaje de programación sino una sintaxis de marcado” se acerca bastante a un flamebait; es un lenguaje declarativo interpretado por cada motor de navegador
    • Estoy de acuerdo con lo dicho, pero HTML no es un formato de presentación sino un lenguaje de descripción de documentos
      Si hablamos de styling, la presentación la maneja CSS
    • Es correcto decir que “la función include es del lado del servidor”. El include del lado del servidor es completamente natural, pero un include del lado del cliente implica que el cliente debe poder modificar el DOM original en un momento que no puede conocer
      Hay dos opciones. La primera es hacerlo durante el parseo de HTML, es decir, antes de crear el DOM, pero no es deseable porque requeriría una solicitud síncrona al servidor para el include. La segunda es que, después de crear el DOM, un elemento específico aparezca en el DOM, se cargue un fragmento de forma asíncrona y luego se reemplace ese elemento del DOM por el fragmento externo, pero eso inutiliza el mecanismo existente de validación de la estructura del DOM. Sin embargo, en el motor Sciter lo implementaron con la primera estrategia. Como el HTML de Sciter normalmente proviene de recursos locales de la app o del sistema de archivos, el costo de solicitar fragmentos adicionales es despreciable
      https://docs.sciter.com/docs/HTML/html-include
  • Como dijeron otros, HTML include tiene varios problemas
    Si main.html incluye child/include1.html, y dentro de child/include1.html hay un enlace src="include2.html", ¿a dónde debería ir el usuario al hacer clic? Si va a include2.html, por el nombre parece una página pensada para include, así que le faltará el resto; y si va a main.html, ¿cómo se indica que esta vez debe usar include2.html en lugar de include1.html? A la inversa, también se podría hacer que article1.html, article2.html y article3.html incluyan cada uno header.html, footer.html y navi.html, pero entonces, si se quiere cambiar globalmente la estructura de todos los artículos, habría que modificar todos los artículos. Si se quiere agregar comments.html a todos los artículos, al final se vuelve a querer generar las páginas con plantillas, y en ese punto ya no se necesita un include en el navegador. También aparecen problemas como que el header necesite conocer el título o el footer necesite conocer los enlaces anterior/siguiente, y se necesita una forma de pasar información entre includes; al final se vuelve a la generación de páginas. Si se analiza, es muy probable que HTML include sea prácticamente inútil para la mayoría de los usos

    • Estos problemas tienen soluciones bastante claras; todos son problemas que se pueden resolver
      Aquí se están mezclando dos casos de uso distintos. Uno es la reutilización de fragmentos y el otro es una isla independiente embebible. Lo segundo ya lo manejan los iframes, así que solo habría que resolver lo primero
    • La lógica de include según la cual include2.html pierde el resto se aplica igual a otros includes
      Si el usuario hace clic en un enlace src="include.css", todo sería un desastre. Puede estar bien para datos estáticos, imágenes, CSS y contenido HTML estático
  • Hay un issue abierto relacionado con esto en WHATWG. También se menciona en la sección de comentarios del post
    Client side include feature for HTML
    https://github.com/whatwg/html/issues/2791

  • HTML tuvo includes y perdieron popularidad
    El término real “include” es una función de XML, y eso es lo que desea el artículo. HTML tenía otro enfoque anterior a XML, que eran los frames. Como los frames hacían mucho más que un include de XML, HTML no obtuvo esa función por separado. Los frames perdieron popularidad por varios problemas, como mal uso, seguridad y accesibilidad

    • A diferencia de Frameset, creo que XML include nunca tuvo buen soporte en muchos navegadores, quizá en ninguno de los principales
      Todavía me gusta usarlo de vez en cuando, pero hace falta una etapa de compilación que lo evalúe antes de pasarlo al usuario o al navegador