-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
What problem are you trying to solve?
When delivering HTML documents, often parts of the document are ready in the server before others. This order might not be the same as the DOM order.
For example, a <select>
might appear early in the DOM, with the <option>
elements for it fetched from a database in parallel, or 3rd party widgets that have a placeholder that gets filled later.
What solutions exist today?
Some frameworks like React & Svelte offer support for out-of-order streaming. React does so by injecting inline <script>
elements that modify the DOM.
How would you solve it?
Suggesting to have a contentname
attribute, and extending the <template>
element to make it into a patch:
<template contentfor="foo">
(a patch) would target an element with the givencontentname=foo
(an outlet).- The first patch replaces the outlet's children, the next ones append (by default).
Additional/supporting APIs
- The
contentmethod
attribute can change the default behavior, with values equivalent to DOM methods (replace-with
,replace-children
,after
,before
,append
,prepend
). - A
contentrevision
attribute can be present on both the patch and the outlet. If they are identical, the patching is skipped. - The outlet lookup is done with the template's parent as the scope, so those patches can't be deeply nested and then target some external element. An exception is that patches that are direct descendants of
<body>
can target the whole document. - In addition, the lookup is tree-scoped and has no affordances for escaping the shadow root.
- Patching into a fragment works, but the same rule applies - which means that the patch cannot escape the fragment.
- The target would receive pseudo-classes that reflect its patching status (
:updating
and:pending
?) - We should consider a JS getter that reflects the current state of patching.
Gritty details:
- Scripts that are part of a patch are executed as normal if performed as part of the main parser.
- Scripts, styles, other RAWTEXT elements (e.g. xmp),
<plaintext>
and the document (HTML) element cannot be directly streamed into. RCDATA elements (title/textarea) can. - From a parser perspective, a setup similar to fragment parsing is set up inside the
<template>
's stack of open items. This makes parsing behave like parsing into the context element and into the<template>
at the same time. The insertion target is the context element. - Conflicts/mismatches are handled as per A way to stream content into an element #2142 (comment)
Alternative: using <script>
with escaping
Using <template>
feels natural as a way to stream HTML. However, it has the constraint of not being able to stream directly into scripts and styles (RAWTEXT).
An alternative would be to have the patch be escaped HTML inside a <script>
element. Then we can use the existing tokenizer state of the script element, unescape the text, and pass it to a second parser that inserts the elements to the correct context.
This however feels unnatural as HTML passed over HTTP should not require escaping :)
Since we can add a replaceWith
mode in the future (instead of the current replaceChildren
mode), it might be a better option than requiring the HTML to be escaped.
Potential future enhancements
- Fetching and patching from an external resource (using the existing
src
attribute))