-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client side include feature for HTML #2791
Comments
I personally do not buy this much (sorry!), as we have enough primitives ( |
How would this differ from HTML Imports? |
HTML Imports load a HTML document, via This idea is about inserting HTML snippet in HTML document. e.g. main document <include src="header.html"></include>
Awesome contents
<include src="footer.html"></include> header.html <h1>Welcome!</h1> footer.html <footer>Copyright 2017 by me</footer> will result in <h1>Welcome!</h1>
Awesome contents
<footer>Copyright 2017 by me</footer> |
I was always dreaming to see this feature in the browser. This tag, as exposed by @TakayoshiKochi should allow to put some HTML content in the DOM in a simple way. I think the I would like to propose the following: <include id="my-include" src="an_URL.html"></include> And the event could be: var included = document.querySelector('#my-include');
included.addEventListener('load', e => {
// ...
});
included.loaded.then((included_) => {
// here you see that
// included_ === included
// and this promise is ready once the HTML code
// from included.src has been fetched and appended to the DOM
}); Reproducing the behavior from So, an include has a small set of features
Hope this idea will be useful. |
There are some questions around this tag that I'd like to expose too.
<include src="an_URL.html">
<div class="preloader">...</div>
</include> After fetching, the innerHTML content should be replaced?
|
I don't think we should do this. The user experience is much better if such inclusion is done server-side ahead of time, instead of at runtime. Otherwise, you can emulate it with JavaScript, if you value developer convenience more than user experience. I'd encourage anyone interested in this to create a custom element that implements this functionality and try to get broad adoption. If it gets broad adoption we can consider building it into the platform, as we have done with other things like jQuery -> querySelectorAll. |
@domenic I tried to develop this idea as a custom element for my projects, and found that it's possible to achieve HTML import, but there are some things that made that solution hard to debug. For instance, beforescriptexecute was removed or even not implemented. Because of that I was forced to turn all my scripts into "inline" scripts. I'll keep on spreading the word with more cases about how to split the code into small pieces without using extra JS effort. |
What's the actual purpose of this? As domenic mentioned, you can already do this quite easily server-side, so why do we need an HTML element to do it less effectively? |
Personally, I found this feature very useful in my projects. But, this is only my personal opinion. And, what @domenic said sounds fair. The only thing that I'd like to repeat is the absence of I'll be happy to share with you @Yay295 or anybody else my experience with this feature, the pros and cons, but that chat should be outside this issue. |
I think it would be quite useful for any cases where we want DRY html authoring but not the burden of running code server side or requiring JS. It's actually what I naively expected html imports to do at first. The use cases may be relegated primarily to the realm of small, static-only websites but I think it's a huge advancement for those cases. Simple static-only sites are a large number of websites, or sites that probably should be purely static but cannot be for reasons such as requiring server side rendering to DRY shared fragments such as header/footer, etc. I'm thinking of all the shared web hosting site builder tools and a large number of wordpress sites (a security/maintenance nightmare for typical site owners in my experience) and things along those lines. These kinds of sites are typically owned/maintained by the least tech-savvy operators and are therefore likely under-represented in these kinds of platform-advancement discussions. I'm aware that dynamic rendering or static build tools can get the job done, but those are inaccessible tools to a majority of simple website owners (again, in my personal experience). The JS-free aspect gets back into the philosophy of progressive enhancement including "site basically works without scripting enabled" and I think that's still important, personally, particularly when we have Brave browser picking up steam with JS disabled by default for security/privacy purposes. I may try to take a stab at faking this using a custom element backed by fetch, but it wouldn't fill the same gap IMHO and would merely be a demonstration for illustrating the convenience it can provide to the page authoring experience once it's all set up. |
I might also comment that I would expect client side includes to do something efficient with caching based on server headers or whatever, minimizing the UX cost of the extra round trips after first load (and I would presume we could also use link rel=preload etc. to great effect for load time beyond the first page). With http/2 implemented appropriately the UX cost of this feature should go away entirely. |
I want to jump in and mention that PHP (Personal Home Page) was literally created to solve this problem "In the most simple way possible". This could be simply done on the browser/markup level so much easier. |
HTML import feature is what big frameworks offer indirectly. I think, if we've this feature then we've more possibilities to write nice things in a simple way. If HTML imports will be present right into the browser then I'll feel that it is a complete framework. |
Further to @brandondees' point, I think I'd point out that offline-first PWAs using Service Worker very much encourage a client-side approach. For example in our PWA (editor.construct.net), despite it being a large and complex web app, we generate virtually nothing on the server side. This is the obvious way to design something that keeps working offline, because everything is static and local, and there's no need for a server to be reachable, especially if all the server is doing is a trivial substitution of content that could easily be done client side. So I think there are actually some significant use cases where you might want to process an include client-side, and "just do it on the server" doesn't cover everything. |
FYI, there was a same discussion happened at WICG/webcomponents#280 |
I've implemented my own very quick-and-dirty demonstration here devpunks/snuggsi#109 to begin experimenting with the pros/cons this feature might have, and we're attempting to keep track of other related efforts for reference as well. @snuggs took it beyond the most basic proof of concept and appears to have brought it close to general production-readiness. I had a discussion recently with a colleague whose initial impression was that this concept merely re-invents server-side includes, which should otherwise be easy enough to work with for most content authors, but I think there are some significant subtle differences still. It's not clear to me why server side includes have not been well leveraged in commonly used website building tools, and I think the reasons boil down to a lack of accessible (read: free) and user-friendly (enough for non tech-savvy users) authoring tools supporting that technology, and lack of standardization. There can be performance benefits from automatically leveraging client side caching of partial documents, which is something I was always baffled by the absence of since I first began learning web dev. New page loads for a given site can retrieve primarily only the portions of the document that are unique, without the need to re-transmit boilerplate sections such as header, navigation, footer, sidebars, etc. without even getting into how the same kinds of benefits also apply when using web component templates. |
Oops - sorry about closing accidentally. I had not been sure about the advantage of client-side processing against server-side include (including PHP's |
Indeed @TakayoshiKochi we created a super simple
I concur with @domenic. on providing a sound iteration/adption/developer ergonomics being worked on in this pull request. The algoritm was as simple as follows. Also works with nested dependencies due to custom elements lifecycle reactions: Element `import-html`
(class extends HTMLElement {
onconnect () {
this.innerHTML = 'Content Loading...'
this.context.location = this.getAttribute `src`
let headers = new Headers({'Accept': 'text/html'})
fetch (this.context.location, {mode: 'no-cors', headers: hdrs})
.then (response => response.text ())
.then (content => this.parse (content))
.catch (err => console.warn(err))
}
parse (string) {
let
root = (new DOMParser)
.parseFromString (string, 'text/html')
.documentElement
, html = document.importNode (root, true)
, head = html.querySelector
`head`.childNodes
, body = html.querySelector
`body`.childNodes
this.innerHTML = ''
this.append ( ... [ ... head, ... body ] )
}
}) Any caveats to Hope this helps @TakayoshiKochi /cc @brandondees |
I was thinking last days since @TakayoshiKochi opened this issue. And found really interesting how to integrate this feature include with |
Ignoring the fact that that code doesn't work, at all, you're really overthinking it. Here's a complete HTML test page. Just change the source to include. <!DOCTYPE html>
<html>
<head>
<script>
class include extends HTMLElement {
connectedCallback() {
fetch(this.getAttribute('src'), {mode: 'cors', credentials: 'same-origin'})
.then(response => response.text())
.then(text => this.outerHTML = text)
.catch(err => console.warn(err));
}
}
customElements.define('include-html', include);
</script>
</head>
<body>
<!-- Include the partial HTML. -->
<!-- If the included HTML has includes, they will be included too. -->
<include-html id="test" src="to_include.html" />
<!-- No problems here either. It just logs an error if this happens. -->
<!-- script>document.getElementById('test').remove()</script -->
</body>
</html> This should be a void element in my opinion. There's nowhere to put any nested elements except after everything, so you might as well just put them outside the include instead. p.s. "Any caveats to |
@Yay295 Nice. But how to execute scripts that are present in The concept of HTML import should be more than just pasting static HTML, right? |
I think the intent here is just to paste DOM content in to another document. HTML imports are a different feature. |
Ok @AshleyScirra . You're right. As consumer, If paste DOM content in to another document then I expect to see |
/cc @brandondees @pachonk |
These are simply strawman / bad faith arguments. If those statements were identifying the true maxims, then why would we have bothered with We've been bundling for 20+ years now, so if server-side were the end-all be-all we would have never gotten baseline In any case, it's turned out that bundlers have been a mostly bad idea. Although the biggest tech companies may be using them effectively, the average websites are loading HUGE packages - the most I've seen for a single top-level package is 37mb (though I'm sure that's just because I don't look often and I'm on fiber - most of us techies never have to deal with phone speeds and when we do, eh, any given web page being broken is just the norm these days). And look at any React site: You want to load a simple form component and, 500mb of node_modules and 2mb of client-side JS later, viola! React is pretty much solving that "simple" problem of PHP, but reimplemented on the fontend... leaving out a substantial number of mishaps, but still almost no one is using it effectively. It's a very, very complex way to include some HTML with a few click and submit handlers. So if we can get to the point were it's easier to do the right thing than the wrong thing - allow developers to fall into the pit of success - imagine how much less complexity we would have. Imagine if including a form component to post a sub to an email marketing list were as simple as a client-side include without node or vite or express or rails? It's deceptive - you think those round trips will get you - but as mentioned, client-side caching can actually be quite good - especially when you can automate it via keeping to existing standards, and when you code more simply it's quite paradoxical - you end up writing more lines of code to get the hello world or define a component... but then total lines of code are several orders of magnitude less. And if the server is truly the right place to do it, well, web servers are already pre-compressing and caching assets. How would having a standard for knowing exactly which HTML files to pre-bundle be a bad thing? That's a win-win-win! |
Removing bundlers doesn't solve that problem. Without a bundler you'd still be loading the same amount of code, but over multiple request/responses. Compression would be hurt too. To avoid loading the whole tree, the developer would need to use multiple entry points, and lazy-load code that isn't immediately. When this is done, bundlers can optimise with this information too. This isn't an argument against client side HTML includes, just pointing out that the argument presented here doesn't make sense. |
Certain frameworks, like alpine, petite-vue, sprae and similar cannot have components because of that limitation. Eg. alpine suggests using server templating for that. |
I completely agree with @coolaj86 Would be a dream being able to have a blog using only static html files and no javascript. And then updating the whole layout (even for old posts) without the need to use Static Site Generators. I think we would not need Static Site Generators anymore as soon as we have HTML includes. Caching and lazy loading would prevent network issues. Also, this specific thread started in 2017. It's been at least 7 years that people are asking for it (probably even longer) |
Just HTML include would not be the answer for your demand. Declarative Custom Element would be. As the site would need not just include but templating. |
The HTML modules discussion this issue was spun off of is still an open issue, but also worth noting this newer somewhat related issue for HTML Module Imports and Exports WICG/webcomponents#1059 See also: WICG/webcomponents#863 |
I just want to add that as @josh mentioned in #2791 (comment), this pattern has been widely used on github.com since 2014, to this day. If you view source of this page, there are 113 instance of |
Hacker News alone has over 779 mentions of Static Site Generators, GitHub has 3,774 repos tagged This abundance proves how much interest there is in purely static sites. And, while many of these generators add extra features like Markdown support or image optimization, most still aren’t simple plug-and-play apps for the average user – their target audience are developers. If your npm, Rubygems, or pip dependencies fall apart or exceed your technical chops, you’re often left looking at paid alternatives or platforms like Squarespace or WordPress. A seemingly trivial-to-diy feature like |
So it seems very clear to me that there is tremendous author interest, and this doesn’t seem like a particularly high effort feature. So what’s the blocker? Theoretical purity? Isn’t that at the very bottom of the PoC? |
I imagine it's blocked on someone doing the spec work. There'll be a lot of devil in the details, eg how to handle typically parser-blocking elements like A good start would be to spec a way for an element to receive a readable stream of text and append the parsed result. Folks will need to figure out how things like |
We don't have to reinvent the wheel here. At the risk of tooting my own horn, I present again my very simple solution to this problem: https://github.com/maherbo/include-HTML-document-fragment It simply requires the addition of an attribute to the anchor element. I personally would like to make it the It loads the external document fragment as an |
Restricting the use of a streaming HTML parser to an attribute on an element is the wrong design here. And a streaming solution is an absolute must. A readable stream solution would allow for things like taking a stream of newline-delimited JSON, transforming it to HTML (eg table rows), and piping it into an element. It would also simplify higher level solutions like the one you maintain, and leave the door open to a higher level platform version. https://extensiblewebmanifesto.org/ I think the reason this issue is unlikely to go anywhere is lots of people want their particular solution to be implemented, and they aren't compatible with other people's solutions. That's why going a little lower-level here makes sense, and it isn't just rubber stamping an API that keeps one person happy. |
"Here", we are discussing how to implement client-side include for an HTML document, not the use of a streaming HTML parser. Creating - or modifying - an attribute or an element is pretty much the only way.
Maybe, but that is entirely for another problem. It is for easing the creation of new implementations in HTML ... like this one. You are focusing on how an HTML parser should work but that doesn't change the fact that HTML has a standard, a defined syntax that means something. Until someone implements this new HTML parser, the current way to do it is by creating custom elements, like I did.
It is the goal of this discussion to identify what should be achieved and how it should be implemented.
You still going to have multiple implementations of client-side include that will be designed by different web developers. If one of them will be incorporated into the HTML standard, it will require some discussion. |
Is there any reason this wouldn't work like the main document? You can do similar things as setting <body>
<section>
<h1>One</h1>
<script>
document.body.innerHTML = '<h1>Boo</h1>';
</script>
<h2>Two</h2>
<script>
console.log('Three');
</script>
</section>
</body> The first In cases that the script does something like: <script>
document.currentScript.parentElement.innerHTML = '<h1>Boo</h1>';
</script> The later content does render and the script does run. Are cases unique to streaming that don't apply to blocking scripts like this? |
This comment was marked as duplicate.
This comment was marked as duplicate.
@justinfagnani let's say the new API is: stream.pipeTo(element.getWritable()); What happens if you try to
If the desired behaviour is one of the last two, then it raises the question: What happens if
The first option is already there spec-wise, and as you say, you can already 'call' |
I'm not really sure what you're on about. The browser already has a streaming HTML parser that's fully specified. You can even access it with JavaScript at a document level: https://youtu.be/LLRig4s1_yA?t=1193 |
You're right, my choice of words is poor. I was trying to connect with your post where you use the term "Restricting the use of a streaming HTML parser to an attribute". The sentence I focus on is:
You are talking about writing HTML using a new standard written in JSON. If I understand you correctly, this will let anyone create their own new way of presenting an HTML document, a way that, if adopted by the community in general, could be integrated into the HTML standard. This is similar to what happened with All I'm saying is that this discussion is about modifying the HTML standard for a particular feature, NOT about a new method for introducing new features in the HTML standard. Or maybe I didn't understand your post as intended. |
I'm not. I think you missed or misunderstood the "would allow for" bit. An attribute-based solution, like I'm not saying that everyone should be doing this, but it would be possible. I'm definitely not saying new formats should be standardised as part of this effort. I'm showing that a lower-level solution is more versatile and enables more use-cases than a higher-level attribute-based solution. |
Can't these two solutions be able to live together, side-by-side? How would you include a document fragment without JavaScript, only in pure HTML? HTML does support embedded content (img, video, iframe, embed, object, etc...). The real question here is why can't you embed a document fragment in the current HTML document? You can embed a whole HTML document, with The objective is to be pure HTML, JavaScript-free. It is to easily manage the caching system to eliminate redundant data requested. It is also to easily manage the content server-side; for example, not having to modify multiple HTML documents that should have the same content written in them, like a menu for example. A client-side include is really about writing efficiently HTML documents without relying on JavaScript. A browser should be able to build an HTML document from different document fragments. It really is just a cut-and-paste process. |
Yes, like I said:
|
I don't think @jakearchibald's comment is an either/or even - if you answer/create the low level thing it would force the answers it seems you'll need for the high level thing (in fact, N potential high level things).. .I don't think that means don't simultaneously try to do the obvious high level thing, or wait until it's shipped and we have lots of things using it... At least, if there is an obvious seeming high level thing we can kind of agree on (I'm not sure it's this, but maybe?). |
I hadn't yet heard new ideas related to potentially locking an element for writing in a long time (outside of the old display locking discussions that became content-visibility). Without this new concept, I would assume that the behavior would just have to be the second option.
So I agree here that this question would need to be settled first, since we don't have either the concept of an active writer, or mid-document streaming updates, and their interaction is important. Without this idea of an active writer, I was assuming that the first option here is the way to go. Is there a place that |
Nah I was just making shit up on the spot 😄 |
Spun off from HTML modules discussion
There are certain amount of interest that including HTML snippet into an HTML document, without using JavaScript. That would be similar to
<iframe>
, but more lightweight and merged into the same document.It would work as a naive way to have your header and footer sections defined in one place.
(Edit by @zcorpan: also see #3681)
The text was updated successfully, but these errors were encountered: