Streaming HTML out of order without JavaScript
lamplightdev.com
lamplightdev.com
<div>
<template shadowrootmode=open>
This is <slot name=a></slot> and <slot name=b>
</template>
<span slot=b>sunny</span>
<span slot=a>funny</span>
</div>
renders as: This is funny and sunny
So it looks like HTML got a bit of a native template system now.What I have been wanting for decades is a native template system in HTML which supports urls. Similar the the script tag, but which loads html:
<html url="/header.html">
<html url="/content.html">
<html url="/footer.html">
I always wondered why this was never implemented. It seems such a basic functionality which would allow to comfortably build websites in a modular way without scripts or serverside processing.It correct, it was only Chrome: https://caniuse.com/?search=html%20import
My memory's hazy, but I remember it becoming more and more complex to maintain as the web's security and performance model evolved, as you needed to manage and secure all of these disparate, discrete DOM trees in the same page context.
Wouldn't what you suggested just been "reinventing" frames/framesets if you added the ability to arbitrarily pull in templates via the network?
There are long and boring reasons why it never went anywhere.
The W3C specced something out around 20 years ago, but browser devs are allergic to all things XML.
I agree partials should be a native feature.
This technique would be a lot more usable though when shadow roots become open style-able. It is kinda funky having to apply your CSS separately to the layout regions that live in your shadow dom.
Wasn't this in HTTP/1.1 (1997), where you could send chunks to anything that could have a target attribute? (As multipart HTML relied on http chunks, which suffered poor support by the Windows http stack, this quickly vanished and in the 2000s support was stripped from browsers. I think, Chrome was the first to do so. Personal note: I once wrote a chat server, which relied on this technology – and it worked pretty well, outside IE.)
Another way this had been once supported was in Netscape 4.0's layers (1997), which could have a `src` attribute and worked just as you would expect. (However, support for this vanished with the first iterations, about the same time JS-styles were switched to CSS. It had been definitely a feature in the NS 4.0 prerelease versions. Especially the capability to re-link the `src` attribute by JS or by a link target was nice.) NS 4 also featured extended HTML entities, which could render JS expressions in place and also provided a mechanism for conditional comments – so, had we followed that route, HTML would have been already a templating language.
xhtml/xslt still works today (though browsers are stuck on v1). It's a declarative template system with a pretty powerful pattern matching system (xpath). You can also extract fragments from external documents or store them into variables. e.g. <xsl:copy-of select="document('/content.html')/body/div[2]/ul/li/a/@href"/> will copy all urls from a list inside the 2nd div. Or instead of second div in the body, you could search for a div with an id or specific attributes or specific children or whatever pattern you want.
Why webdevs forsook xml will always be a mystery to me. Particularly given that they now use things like JSX.
I think this was at the time I was learning jquery and using css selector syntax for finding elements, and the difference between that and xpath was pretty stark.
I basically (probably with insufficient knowledge) filed xslt away with vb and tables for formatting as old, complex and soon to be replaced
Besides some papercuts / browser bugs like "refreshing the page desyncs the inspector", the main issue I find with it is I don't see how I can modify things dynamically?
By the time JS runs, it's on the resulting HTML DOM, not the XML source, so I can't add elements dynamically respecting the XSLT stylesheet?
Also, XML must be well-formed and have a single root, I can't stream it out on the server piecewise?
With those 2 limitations, I don't see any advantages over any other server-side templating language, it just duplicates work that could happen once in a SSG or at least cached in a CDN, onto every client computer.
The main advantage for simple things is that you don't need an application server or compiler. It's all static files, and it's all built right into the browser so easy to get started with. For less simple things, it should be easier to cache since you can separate out user data and have the bulk of the information you send be the static template (which the user can cache).
I suppose maybe that last point is why people use javascript frameworks for static pages (so they can send a static, cached js file and then small payloads for user data), which seems like overkill to me.
It would be nice if browsers supported streaming xml. Of course streaming xml exists (e.g. XMPP). It's not really any different than json: the spec said you need to have a single root object, and then someone wrote a spec that says you don't need to do that.
You make a good point of caching the XSLT file itself though, I hadn't considered that. Simple server outputs XML data, browser renders into a page.
There are also [Server Side Includes](https://en.wikipedia.org/wiki/Server_Side_Includes) which were used for that.
Funny enough, I've used SSIs first and last time around 2020. The project I worked on had a heavily cached landing page (for performance reasons), but needed a way to swap out the homepage banners based on product releases, and the easiest way around that was to output a SSI directive for a separate banners endpoint.
There's a long-standing WHATWG feature request open for client-side includes here: https://github.com/whatwg/html/issues/2791
And several userland custom element implementation, like https://www.npmjs.com/package//html-include-element
One of the cool things that you can do with client-side includes and shadow DOM is render the included HTML into a shadow root that has <slot>s, so that the child content of the include element is slotted into a shell implemented by the included HTML.
This lets you do things like have the main page be the per-page content and the included HTML be a heavily cached site-wide shell, and then another per-user include with personalized HTML - all cached appropriately.
If you can do your includes at page load time, you've been able to do this for 25 years with xslt; it's been built into the browser almost since the beginning of the web!
I've played around with doing exactly that: include a `/myinfo.xml` document that has information about your user, and then the rest of the page template just grabs `$myinfo/user/@name` or whatever wherever it needs. The neat thing is it has graceful degradation by default: if the request fails, then your include will be empty and you can treat it as a logged out page. So you can e.g. display the username and logout button in the top right if logged in, or a login button if not. You can also e.g. include a CSRF token in your info document and plop that into any forms in your page by just doing `value="{$myinfo/csrf-token}"` or whatever.
This would have been better than world peace. But this threatened a lot of warmonger advertisers. So it was "deprecated" and then obliterated.
<div id=t>
<template shadowrootmode=open>
<slot name="s1">s1</slot>
<slot name="s2">s2</slot>
<slot name="s3">s3</slot>
</template>
</div>
<script>
setTimeout(_=> t.innerHTML += `<span slot="s3">three</sp>`, 1000)
setTimeout(_=> t.innerHTML += `<span slot="s1">one</sp>`, 2000)
setTimeout(_=> t.innerHTML += `<span slot="s2">two</sp>`, 3000)
setTimeout(_=> t.innerHTML += `<span slot="s1">ONE!!</sp>`, 4000)
</script>
I guess there is no way to replace a slot, again and again?it simplifies things a lot: js enabled out-of-order streaming leads to SEO problems and frameworks usually need to come up with workarounds - detect bots and turn of streaming for that case.
With such technique no workaround is needed, less things to worry about.
Excellent!
The underlying `swtl` library is itself pretty interesting.
Note the use of `delayed(...)` in the blog's example code: a promise in a tagged template.
SWTL's `html` allows for this (I think the blog's example follows the simple case here https://github.com/thepassle/swtl/blob/main/html.js#L23). And SWTL's `render` method races the promises (https://github.com/thepassle/swtl/blob/main/render.js#L120) outward.
Changing the functionality now would break those sites, adding a new CSS prop would make the fix opt-in.
But with everything in ARIA, it always depends on real-world support of screen readers, which is very poor. You have to work with what actually works unfortunately.
Imo changing order just to change the streaming order for a tiny performance gain is not worth breaking the UX for people with accessibility needs.
[1] https://developer.mozilla.org/en-US/docs/Web/Accessibility/A... [2] https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...
Demo: https://enamel.pages.dev
First thought is [mutation observer](https://developer.mozilla.org/en-US/docs/Web/API/MutationObs...) but that seems too deep into the weeds
It's a log file that starts with a header: just a script tag with some JS, followed by a special string (I used a special HTML comment as a boundary indicator). The server then appends its logging to this file in a web hosted directory.
The JS periodically does an XHR of it's own location.href, polling itself. It then splits on the special boundary string, thus collecting the latest log data, parses it to generate pretty/coloured/linked HTML, and updates the current page according (and controls scrolling if required).
This gives you a log file that's continuously being appended to, but can be visited in the browser using any static file serving and automatically updates as it's populated.
Alternatively, I think slotting raises an event, so you could use js to remove the pre-existing content when new content gets added
However previously the limitation was that the content needed to be more or less in order (you could use some CSS tricks but they were limited). Using this trick I would be able to render the full list of pending feeds then insert a result as each finishes being fetched.
In fact it seems that for each slot the last element will be used. So you can even create live-updating pages based on this which is really cool. For example imaging you had a score ticker. You could push an update to that region of the page every time the score changes.
I think this is definitely a niche use case, but it is very nice to support this without JS. Of course in all but the simplest use cases JS may still be the right solution. For example if the connection drops for any reason there is no mechanism for showing the user an error, let alone retrying.
For a comparison, basically the entire bajillions of man-hours that have gone into React Server Components [1], and all the many, many complications and footguns inherent in their approach, is trying to solve the same problem.
[1]: https://nextjs.org/docs/app/building-your-application/render...
view-source:https://ooo.lamplightdev.workers.dev/
then watch the bottom of the page as the new `<span slot=...>` elements come in. As they come in, the browser's, well, slotting the contents into the corresponding slot in the template.
Yeah, I am one of the few outliers who still host JS-free out of cybersecurity reasons.
What's the browser support like?
> All browsers support streaming HTML
And the caniuse is promising: https://caniuse.com/?search=slot
But you can already do a lot with regular streaming and using CSS Grid, inline <script> tags, etc to move stuff around the page.
https://caniuse.com/mdn-html_elements_template_shadowrootmod...
If that is the reason, it'll provide an (even more than usually) poor experience for those behind such a proxy
Alternatively I could imagine some kind of proxy between you and the site buffering things up.
Yeah I think that might be it, because it works fine on FF (iOS) outside.