Including HTML Files from Other HTML Files, on the File:// System


“I wont be sending an officer because your not in any danger at all. You have obviously just put a blanket on a dog while it is sitting in your car and taken a photo. “

I still have a passion for web apps that run on the file system. It’s an extremely easy development model and extremely flexible. You can send a file (or set of files) to anyone and be confident they can open the files and run your web app, regardless of their operating system and without imposing on them the requirement of setting up a server. Furthermore, they can stick it on a share drive and BAM, guerilla multi-user system. I’ve had the habit long before I developed for TiddlyWiki but my time with TiddlyWiki focused my attention on the benefits and taught me a number of Single-Page App (SPA) hacks which most web developers are still oblivious to.

And let the SPA hacks roll on …

As I start to think about resetting the slideshow framework I’ve been randomly sniping at conferencesrecently, one thing I’d like to do is the idea of a file per Master Slide, containing all of the HTML, JavaScript, and CSS. This is more or less how TiddlyWiki themes work, and a very neat modularisation tactic.

Unfortunately, HTML – bless it – can include JavaScript (<script src="something.js">) and CSS (<link href="something.css"> etc), but not HTML (which would look something like <div src="something.html"> in my dreams). So what are the options for pulling in one HTML file from another HTML file:

  • Server-side includes: We’ve long had server-side includes. I powered my homepage from this less-than-stellar technique for modularisation around 15 years ago. The problem is none too hard to derive from their name. Server, I don’t want one.
  • XMLHttpRequest: We could make a XHR call and actually this is possible from file to file. Unfortunately, Google Chrome (and maybe others?) sees each file as belonging to a separate domain, making it impossible, and other browsers may issue a warning or confirmation, making it obtrusive.
  • File APIs: Again, we could use the magic of $.twFile to read the other file. But this relies on browser-specific hacks and they have to be degraded to a separate Java applet, which requires a proper Java installation, in the case of Chrome, Safari, Opera, and others. Firefox uses Moz-specific API and IE uses ActiveX, which are good but also incur warnings and may be blocked by firewalls. Still, it’s not a bad solution. The extra Java applet is a big downside in TiddlyWiki, because you suddenly need to send around two files instead of one, but here I’m already assuming there’s a bundle of files to be sent around.
  • Outputting HTML inside JavaScript: Since we can read Javascript, we could just spit out the HTML from JavaScript. The benefit here is it works, and works for the most ancient of browsers. But it would require a lot of string manipulation, which would look minging and be unmaintainable, and I massively value elegant code (or at least, the possibility of it). Many times I have wished JavaScript supported Here Docs, but alas, it doesn’t :(. The best you get is a long sequence of lines ending in . Unacceptable. You can also achieve this kind of thing with E4X, but that’s not widely supported.
  • Hiding HTML in JavaScript or CSS: I’ve considered tricks like embedding the entire HTML inside a JavaScript or CSS comment, but the problem is the same reason we need JSONP; when you source a JS or CSS file, your app feels the effects of it, but your code doesn’t get to see the source. I’m still holding a candle for the possibility of some CSS hack, like based on computed style, which would let you trick the browser into thinking the background colour of a button is an entire HTML document or something…which would be worth doing just for the sake of being insanely ace.
  • Or. iFrames.

Thinking it through, I decided iFrames are your friend. You embed the file to be included as a (hidden) child iFrame. This can work in a couple of ways.

The parent could read the DOM directly:

javascript

  1. var dom = document.querySelector("iframe").contentWindow.document;
  2.     document.querySelector("#messageCopy").innerHTML = dom.querySelector("#message").innerHTML;
(The child contains message element, the parent contains messageCopy.)

This works on Firefox, but not Chrome, because Chrome sees each file as belonging on a separate domain (as I said above, wrt XHR). So we need to make a cross-domain call. We could be AWESOME and use the under-loved Cross-Origin Resource Sharing (CORS) capability to make cross-domain XHR calls, but in this case, it doesn’t work because it involves HTTP headers, and we’re doing this with pure files.

The solution, then, is another kind of iFrame technique: Cross-domain iFrames. It’s been possible to do cross-domain iFrame communication for a while, but fortunately, modern browsers provide an explicit “HTML5” API for cross-domain iframe communication. I tested it in Chrome, and it works. On Files. Yay.

Under this paradigm, “index.html” contains:

  1. <script>
  2.   window.onload = function() {
  3.     window.addEventListener("message", function(e) {
  4.       document.querySelector("#messageCopy").innerHTML = e.data;
  5.     }, false);
  6.    document.querySelector("iframe").contentWindow.postMessage(null, "*");
  7.   };
  8. </script>
  9. <h1>Test parent</h1>
  10. <div id="messageCopy"></div>
  11. <iframe src="included.html"></iframe>

while “included.html” contains:

  1. <script>
  2.   window.addEventListener("message", function(e) {
  3.     e.source.postMessage(document.getElementById("message").innerHTML, "*");
  4.   }, false);
  5. </script>
  6. <div id="message">This is the message</div>

Point your spiffy HTML5 browser to index.html and watch in glee as the message gets copied from included to includer. I wasn’t sure it would work, because certain other things – like Geolocation and Workers – simply don’t work in all browsers against the File:// URI, even though they probably should. (Probably because the browsers keep mappings of permissions to each domain, and these systems assume the domain is served with HTTP(s).)

This technique will also degrade to older browsers using those “pre-HTML5 hacks. (As the Romans used to say, Omnis enim API HTML V, aequivalet HTML V pre-furta..)

So I’m glad this technique works and intend to use it in the future, nicely abstracted with a library function or two.

3 thoughts on Including HTML Files from Other HTML Files, on the File:// System

Leave a Reply