Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This was the rabbit hole that I started down in the late 90s and still haven’t come out of. I was the webmaster of the Analog Science Fiction website and I was building tons of static pages, each with the same header and side bar. It drove me nuts. So I did some research and found out about Apache server side includes. Woo hoo! Keeping it DRY (before I knew DRY was a thing).

Yeah, we’ve been solving this over and over in different ways. For those saying that iframes are good enough, they’re not. Iframes don’t expand to fit content. And server side solutions require a server. Why not have a simple client side method for this? I think it’s a valid question. Now that we’re fixing a lot of the irritation in web development, it seems worth considering.



Server-side includes FTW! When a buddy and I started making "web stuff" back in the mid-90s the idea of DRY also just made sense to us.

My dialup ISP back then didn't disable using .htaccess files in the web space they provided to end users. That meant I could turn on server-side includes! Later I figured out how to enable CGI. (I even went so far as to code rudimentary webshells in Perl just so I could explore the webserver box...)


I've become a fan of https://htmx.org for this reason.

A small 10KB lib that augments HTML with the essential good stuff (like dynamic imports of static HTML)


Seems like overkill to bring in a framework just for inlining some static html. If that's all you're doing, a self-replacing script tag is neat:

    <script>
      function includeHTML(url) {
        const s = document.currentScript
        fetch(url).then(r => r.text()).then(h => {
          s.insertAdjacentHTML('beforebegin', h)
          s.remove()
        })
      }
    </script>
...

    <script>
      includeHTML('/footer.html')
    </script>
The `script` element is replaced with the html from `/footer.html`.


this here is the main idea of HTMX - extended to work for any tag p, div, content, aside …

there are many examples of HTMX (since it is a self contained and tiny) being used alongside existing frameworks

of course for some of us, since HTMX brings dynamic UX to back end frameworks, it is a way of life https://harcstack.org (warning - raku code may hurt your eyes)


If you want it more straight-forward and simple hypermedia approach, then check out https://data-star.dev (highly recommended, there are great youtube video's where the maintainers discuss their insights). Following up where htmx took things.


But this requires JavaScript...


> But this requires JavaScript...

Depending on the specific objection to Javascript, this may or may not matter:

1. You object to any/all JS on a page? Yeah, then this won't work for you.

2. You object to having to write JS just to get client-side includes? This should mostly work for you.

It all depends on what the actual objection is.


... but the folks behind that standard don't want to encourage browsing with Javascript off.


Why would the ES standards organization that defines JavaScript syntax encourage people not to use it?


The minified version needs ~51 kilobytes (16 compressed):

  $ curl --location --silent "https://unpkg.com/htmx.org@2.0.4" | wc -c
  50917
  
  $ curl --location --silent "https://unpkg.com/htmx.org@2.0.4" | gzip --best --stdout | wc -c
  16314


see fixi if you want bare-bones version of the same idea:

https://github.com/bigskysoftware/fixi


> Iframes don’t expand to fit content

Actually, that was part of the original plan - https://caniuse.com/iframe-seamless


I used the seamless attribute extensively in the past, it still doesn't work the way GP intended, which is to fit in the layout flow, for example to take the full width provided by the parent, or automatically resize the height (the pain of years of my career)

It worked rather like a reverse shadow DOM, allowing CSS from the parent document to leak into the child, removing borders and other visual chrome that would make it distinguishable from the host, except you still had to use fixed CSS layouts and resize it with JS.


I mean in 1996s netscape you could do this (I run the server for a website that still uses this):

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
    <html>
      <frameset cols="1000, *">
        <frame src="FRAMESET_navigation.html" name="navigation">
        <frame src="FRAMESET_home.html" name="in">
      </frameset>
    </html>
  
The thing that always bugged me about frames is that they are too clever. I don't want to reload only the frame html when I rightclick and reload. Sure the idea was to cache those separately, but come on — frames and caching are meant to solve two different problems and by munching them together they somewhat sucked at solving either.

To me includes for HTML should work in the dumbest way possible. And that means: Take the text from the include and paste it where the include was and give the browser the resulting text.

If you want to cache a nav section separately because it appears the same on every page lets add a cache attribute that solves the problem independently:

  <nav cache-id="deadbeefnav666">
    <some-content></etc>
  </nav>
  
To tell the browser it should load the inner html or the src of that element from cache if it has it.

Now you could convince me thst the include should allow for more, but it being dumb is a feature not a bug.


Nitpick: the HTML4 spec was released in December 1997, and HTML4.01 only in December 1999 so it probably wouldn't have run in 1996s Netscape.


The doctype doesn’t matter in this context. Netscape Navigator 2 supported frames in 1995 and would render that page.


Back then it was common for Netscape to have features that (years) later became standard HTML.


The optimal solution would be using a template engine to generate static documents.


> The optimal solution would be using a template engine to generate static documents.

This helps the creator, but not the consumer, right? That is, if I visit 100 of your static documents created with a template engine, then I'll still be downloading some identical content 100 times.


XSLT solved this problem. But it had poor tool support (DreamWeaver etc) and a bunch of anti-XML sentiment I assume as blowback from capital-E Enterprise stacks going insane with XML for everything.

XSLT did exactly what HTML includes could do and more. The user agent could cache stylesheets or if it wanted override a linked stylesheet (like with CSS) and transform the raw data any way it wanted.


The Umbraco CMS was amazing during the time that it used and supported XSLT.

While it evaluated the xslt serverside it was a really neat and simple approach.


I'll still be downloading some identical content 100 times.

That doesn't seem like a significant problem at all, on the consumer side.

What is this identical content across 100 different pages? Page header, footer, sidebar? The text content of those should be small relative to the unique page content, so who cares?

Usually most of the weight is images, scripts and CSS, and those don't need to be duplicated.

If the common text content is large for some reason, put the small dynamic part in an iframe, or swap it out with javascript.

If anyone has a genuine example of a site where redundant HTML content across multiple pages caused significant bloat, I'd be interested to hear about it.


I care! It is unnecessary complexity, and frankly ugly. If you can avoid repetition, then you should, even if the reason is not obvious.

To give you a concrete example, consider caching (or, equivalently, compiling) web pages. Maybe you have 100 articles, which share a common header and footer. If you make a change to the header, then all 100 articles have to be uncached/rebuilt. Why? Because somebody did not remove the duplication when they had the chance :-)


Compression Dictionary Transport [0] seems like something that can potentially address this. If you squint, this looks almost like XSLT.

[0] https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Com...


True for any server side solution, yes.

On the other hand it means less work for the client, which is a pretty big deal on mobile.


macros!


You can message the page dimensions to the parent. To do it x domain you can load the same url into the parent with the height in the #location hash. It won't refresh that way.


I know it’s possible to work around it, but that’s not the point. This is such a common use case that it seems worthwhile to pave the cowpath. We’ve paved a lot of cowpaths that are far less trodden than this one. This is practically a cow superhighway.

We’ve built an industry around solving this problem. What if, for some basic web publishing use cases, we could replace a complex web framework with one new tag?


I couldn't agree more.

<div src="foo.txt"></div>


https://www.w3.org/TR/xhtml2/introduction.html

> XHTML 2 takes a completely different approach, by taking the premise that all images have a long description and treating the image and the text as equivalents. In XHTML 2 any element may have a @src attribute, which specifies a resource (such as an image) to load instead of the element.


The content of the div can be used to support legacy browsers. It can have a link, iframe, a message or an outdated version of the content/menu/header/footer etc


> We’ve built an industry around solving this problem. What if, for some basic web publishing use cases, we could replace a complex web framework with one new tag?

I actually did that replacement, with a few enhancements (maybe 100 lines of code, total?). It's in arxiv pending at the moment. In about two days it will be done and I'll post a Show HN here.


Taps foot....


> Woo hoo! Keeping it DRY (before I knew DRY was a thing)

I still remember the script I wrote to replace thousands (literally) slightly different headers and footers in some large websites of the 90s. How liberating to finally have that.


Don't Service Workers API provide this now, essentially act like a in-browser proxy to the server.

https://developer.mozilla.org/en-US/docs/Web/API/Service_Wor...


Rational or not, some of us try very hard to avoid JavaScript based solutions.


As a dev from the early 90s, I share the sentiment. Watching javascript become more and more complex and bloated for little to no benefit to the end user.


> Why not have a simple client side method for this?

Like writing a line of js?


A line of JS that has to run through the Javascript interpreter in your browser rather than a simple I/O operation?

If internally this gets optimized to a simple I/O operation (which it should) then why add the JS indirection in the first place?


> simple I/O operation

That’s the reason it doesn’t get implemented. Nobody wants the simple I/O operation based inclusion. The moment you try to propose it, there’ll be demands to add conditional logic or macros. More relevant for the web is instantiating html templates, which will undoubtedly get piled onto such a feature. And pretty soon you have yet another:

> interpreter in your browser

Might as well use the one already there.


The difference between "a line of JS" and a standardized declarative solution is of course that a meek "line of $turing_complete_language" can not, in the general case, be known and trusted to do what it purports to do, and nothing else; you've basically enabled any kind of computation, and any kind of behavior. With an include tag or attribute that's different; it's behavior is described by standards, and (except for knowing what content we might be pulling in) we can 100% tell the effects from static analysis, that is, without executing the code. With "a line of JS" the only way, in the general case, to know what it does is to run it (an infinite number of times). Also, because it's not standardized, it's much harder to save to disk, to index and to archive it.


A block of in-line JavaScript stops the renderer until it runs because its output cannot be determined before it completes.


> A block of in-line JavaScript stops the renderer until it runs because its output cannot be determined before it completes.

I do it in a way that doesn't stop the renderer.


So would any form of html inclusion.


Unless the renderer starts after all HTML is retrieved, or the container element has a defined size.


In which case you have reinvented the <iframe>


The web seems like it was deliberately designed to make any form of composability impossible. It’s one of the worst things about it as a platform.

I’m sure some purist argument has driven this somewhere.


I think of all the “hygienic macro” sorts of problems. You really ought to be able to transclude a chunk of HTML and the associated CSS into another document but you have to watch out for ‘id’ being unique never mind the same names being used for CSS classes. Figuring out the rendering intent for CSS could also be complicated: the guest CSS might be written like

   .container .style { … }
Where the container is basically the whole guest document but you still want those rules to apply…. Maybe, you want the guest text to appear in the same font as the host document but you still want colors and font weights to apply. Maybe you want to make the colors muted to be consistent with the host document, maybe the background of the host document is different and the guest text isn’t contrasts enough anymore, etc.


I look back longingly at the promise of XML services in the early days of Web 2.0. Before the term just meant JavaScript everywhere.

All sorts of data could be linked together to display or remix by user agents.


HTML is a markup language, not a programming language. It's like asking why Markdown can't handle includes. Some Markdown editors support them (just like some server-side tools do for HTML), but not all.


Including another document is much closer to a markup operation than a programming operation. We already include styles, scripts, images, videos, fonts...why not document fragments?

Markdown can't do most of those, so it makes more sense why it doesn't have includes, but I'd still argue it definitely should. I generally dislike LaTeX, but about the only thing I liked about it when writing my thesis was that I could have each chapter in its own file and just include all of them in the main file.


This isn’t programming. It’s transclusion[0]. Essentially, iframes and images are already forms of transclusion, so why not transclude html and have the iframe expand to fit the content?

As I wrote that, I realized there could be cumulative layout shift, so that’s an argument against. To avoid that, the browser would have to download all transcluded content before rendering. In the past, this would have been a dealbreaker, but maybe it’s more feasible now with http multiplexing.

[0] https://en.m.wikipedia.org/wiki/Transclusion#Client-side_HTM...


With Early Hints (HTTP code 103), it seems especially feasible. You can start downloading the included content one round-trip after the first byte is sent.


Well, asciidoc - a markup language supports includes, so the "markup languages" analogy doesn't hold.

https://docs.asciidoctor.org/asciidoc/latest/directives/incl...


That’s the Hyper part of HTML, and what makes it special.

It’s made to pull in external resources (as opposed to other document formats like PDF).

Scripts, stylesheets, images, objects, favicons, etc. HTML is thematically similar.


No, HTML is fundamentally different because (for a static site without any JS dom manipulation) it has all the semantic content, while stylesheets, images, objects, etc. are just about presentation.


Images are content. Videos are content. Objects/iframes are content.

The only one that is presentational is stylesheets.


Which (as I'm sure you know), also literally has 'content' :)

https://developer.mozilla.org/en-US/docs/Web/CSS/content


True :)


Images and videos are not semantic content. The alt attributes that describe them on the other hand are indeed semantic content.


Can you describe what semantic, non-textual content would be?


> Images and videos are not semantic content

Something in that tenet does not compute with me.


I think the distinction is "semantic on what level/perspective?". An image packaged as a binary blob is semantically opaque until it is rendered. Meanwhile, seeing <img> in the HTML or the file extension .jpg in any context that displays file extensions tells me some information right out of the gate. And note that all three of these examples are different information: the HTML tag tells me it's an image, whereas the file extension tells me it's a JPEG image, and the image tells me what the image contains. HTML is an example of some kind of separation, as it can tell you some semantic meaning of the data without telling you all of it. Distinguishing and then actually separating semantics means data can be interpreted with different semantics, and we usually choose to focus on one alternative interpretation. Then I can say that HTML alone regards some semantics (e.g. there is an image here) while disregarding others (e.g. the image is an image of a brick house).


I'm not sure what isn't computing. Presumably you know (or have looked up) the meaning of "semantic"? Images and videos are graphic, not semantic, content. To the extent they are rendering semantic content, that content should be described in the alt tag.


Iframes exist.


I think this is the most likely answer.

I'm not defending it, because when I started web development this was one of the first problems I ran into as well -- how the heck do you include a common header.

But the original concept of HTML was standalone documents, not websites with reusable components like headers and footers and navbars.

That being said, I still don't understand why then the frames monstrosity was invented, rather than a basic include. To save on bandwidth or something?


Frames were widely abused by early web apps to do dynamic interfaces before XHR was invented/widely supported. The "app" had a bunch of sub-frames with all the links and forms carefully pointing to different frames in the frameset.

A link in a sidebar frame would open a link in the "editor" frame which loaded a page with a normal HTML form. Submitting the form reloaded it in that same frame. Often the form would have multiple submit buttons, one to save edits in progress and another to submit the completed form and move to the next step. The current app state was maintained server side and validation was often handled there save for some basic formatting client side JavaScript could handle.

This setup allowed even the most primitive frame-supporting browsers to use CRUD web apps. IIRC early web frameworks like WebObjects leaned into that model of web app.


Oh my goodness, yes you're right, I'd forgotten entirely about those.

They were horrible -- you'd hit the back button and only one of the frames would go back and then the app would be in an inconsistent state... it was a mess!


You needed to hit the reset button (and hoped it worked) and never the back button! Yes, I suffered through early SAP web apps built entirely with frames and HTML forms. It was terrible.

I don't love JavaScript monstrosities but XHR and dynamic HTML were a vast improvement over HTML forms and frame/iframe abuse.


Really well written web form applications were a delight in 2001 and a large improvement over conventional applications written in Windows. It helped that application data was in a SQL database, with a schema, protected by transactions, etc as opposed to a tangle of pointers that would eventually go bad and crash the app -- I made very complicated forms for demographic profiling, scientific paper submission, application submission, document search, etc. If you did not use "session" variables for application state this could at worst cause a desynchronization between the browser and the server which (1) would get resynchronized at any load or reload and (2) never get the system into a "stuck" state from the user viewpoint and (3) never lose more than a screen full of work.

Try some other architecture though and all bets were off.

Amazon's web store looked and worked mostly the same as it does now, people were very impressed with MapQuest, etc.

Applications like that can feel really fast, almost desktop application fast, if you are running them on a powerful desktop computer and viewing them on another computer or tablet over a LAN


To be fair, modern SAP web apps are also terrible.


The original concept of HTML was as an SGML subset, and SGML had this functionality, precisely because it's very handy for document authoring to be able to share common snippets.


A lot of early HTML was about taking the output of a different system such as a mainframe and putting that output into HTML.

Lots of gateways between systems.


Markdown doesn't have this common HTML pattern of wanting to include a header/footer in all pages of a site.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: