Skip to content

Commit 4ab0a51

Browse files
committed
Update explainer with latest proposed syntax
1 parent 9975070 commit 4ab0a51

File tree

1 file changed

+98
-16
lines changed

1 file changed

+98
-16
lines changed

patching-explainer.md

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Interleaved HTML streaming (patching)
22

33
## Motivation
4+
45
Streaming of HTML existed from the early days of the web, serving an important purpose for perceived performance when loading long articles etc.
56
However, it always had the following major constraints:
67
1. HTML content is streamed in DOM order.
@@ -15,41 +16,93 @@ This proposal introduces partial out-of-order HTML streaming as part of the web
1516

1617
## Declarative patching
1718

18-
Patches are delivered using a `<template>` element with the `contentmethod` attribute and target an existing elements in the DOM with the `contentname` attributes. These patches require no scripts to apply (are declarative) and can appear in the main response HTML to support out-of-order streaming.
19+
Patches are delivered using a `<template>` element with the `for` attribute and target an existing elements in the DOM with the `sink` attribute. These patches require no scripts to apply (are declarative) and can appear in the main response HTML to support out-of-order streaming.
1920

2021
Patches can be be applied later in the page lifecycle using JavaScript, see [script-initiated patching](#script-initiated-patching).
2122

2223
### Proposed markup
2324

24-
The `contentname` attribute is used to identify an element which can be patched:
25+
The `sink` attribute is used to identify an element which can be patched:
2526

2627
```html
27-
<section contentname=gallery>Loading...</section>
28+
<section sink="gallery">
29+
Loading...
30+
</section>
2831
```
2932

3033
The content is then patches using a `<template>` element:
3134

3235
```html
33-
<template contentmethod="replace-children">
34-
<section contentname=gallery>Actual gallery content<section>
36+
<template for="gallery">
37+
Actual gallery content
3538
</template>
3639
```
3740

38-
The element name (`section`) needs to be repeated so that children are parsed correctly, but only the child nodes are actually replaced in this example.
41+
The existing children of the `<section>` element ("Loading...") are removed and the final DOM will be:
3942

40-
There are two proposed `contentmethod` values:
43+
```html
44+
<section sink="gallery">
45+
Actual gallery content
46+
</section>
47+
```
4148

42-
- `append` inserts nodes at the end of the element, similar to `element.append(nodes)`.
43-
- `replace-children` replaces any existing child nodes, similar to `element.replaceChildren(nodes)`.
49+
A new `<!marker>` node is introduced to give more granular control over which nodes are replaced. With a single marker, the marker node is replaced.
4450

45-
At a low level, the only difference is that is `replace-children` removes existing nodes and then appends new nodes, while `append` only appends new nodes.
51+
```html
52+
<ul sink="list">
53+
<li>first item</li>
54+
<!marker>
55+
<li>last item</li>
56+
</ul>
57+
58+
<template for="list">
59+
<li>middle item</li>
60+
</template>
61+
```
4662

47-
A few details about interleaved patching:
48-
- Templates with a valid `contentmethod` are not attached to the DOM.
49-
- If the patching element is not a direct child of `<body>`, the outlet has to have a common ancestor with the patching element's parent.
50-
- The patch template has to be in the same tree (shadow) scope as the outlet.
63+
To replace a range of nodes, `start` and `end` attributes are used:
5164

52-
See the https://github.com/whatwg/html/pull/11818 for the full processing model and details.
65+
```html
66+
<section sink="section">
67+
<header>header stuff</header>
68+
<!marker start>
69+
Placeholder content
70+
<!marker end>
71+
<footer>footer stuff</footer>
72+
</section>
73+
74+
<template for="section">
75+
<p>Real content</p>
76+
</template>
77+
```
78+
79+
Finally, to support multiple ranges, marker nodes can have a `name` attribute. The names must match one of the tokens in the `sink` attribute, and any number of ranges can be exposed:
80+
81+
```html
82+
<div sink="part-one part-two">
83+
<!marker start name="part-one">
84+
Placeholder content
85+
<!marker end name="part-one"
86+
<hr>
87+
<!marker start name="part-two">
88+
Placeholder content
89+
<!marker start name="part-two">
90+
</div>
91+
92+
<template for="part-one">
93+
<p>Actual 1st part of the content</p>
94+
</template>
95+
96+
<template for="part-two">
97+
<p>Actual 2nd part of the content</p>
98+
</template>
99+
```
100+
101+
A few details about patching:
102+
103+
- Templates with a valid `for` attribute are not attached to the DOM, while templates that don't apply are attached to signal an error.
104+
- If the patching element is not a direct child of `<body>`, the sink has to have a common ancestor with the patching element's parent.
105+
- The patch template has to be in the same tree (shadow) scope as the sink.
53106

54107
### Interleaved patching
55108

@@ -283,7 +336,7 @@ Possible ergonomic additions this option:
283336
### Avoiding overwriting with identical content
284337

285338
Some content might need to remain unchanged in certain conditions. For example, displaying a chat widget in all pages but the home, but not reloading it between pages.
286-
For this, both the outlet and the patch can have a `contentrevision` attribute. If those match, the content is not applied.
339+
For this, both the sink and the patch can have a `contentrevision` attribute. If those match, the content is not applied.
287340

288341
### Streaming to non-element ranges
289342

@@ -329,6 +382,35 @@ This can be done with a `patchsrc` attribute.
329382

330383
Enabling remote fetching of patch content would act as a script in terms of CSP, with a CORS-only request, and would be sanitized with the same HTML/trusted-types restrictions as patching using script.
331384

385+
## Alternatives considered
386+
387+
### `contentmethod` attribute
388+
389+
An earlier proposal that did not have marker nodes used a `contentmethod` attribute to control which nodes are removed and where new nodes are inserted. The `contentname` attribute was used on both `<template>` and the target element to link them.
390+
391+
Example:
392+
393+
```html
394+
<section contentname=gallery>Loading...</section>
395+
396+
<template contentmethod="replace-children">
397+
<section contentname=gallery>Actual gallery content<section>
398+
</template>
399+
```
400+
401+
These `contentmethod` values could be supported:
402+
403+
- `append` inserts nodes at the end of the element, similar to `element.append(nodes)`.
404+
- `prepend` inserts nodes at the end of the element, similar to `element.prepend(nodes)`.
405+
- `replace-children` replaces any existing child nodes, similar to `element.replaceChildren(nodes)`.
406+
- `replace` replaces the element itself, similar to `element.replaceWith(nodes)`.
407+
408+
Weaknesses of this design are:
409+
410+
- Doesn't support replacing arbitrary ranges of nodes, only an element or all of its children.
411+
- In order to support patching `<title>`, which uses the [RCDATA tokenizer state](https://html.spec.whatwg.org/multipage/parsing.html#rcdata-state), the tag name of the target element must be repeated. TODO
412+
- `prepend` can fail if the original first child of the element is removed, meaning that a patch can fail mid-stream, requiring some error handling/reporting.
413+
332414
## [Self-Review Questionnaire: Security and Privacy](https://w3c.github.io/security-questionnaire/)
333415

334416
1. What information does this feature expose,

0 commit comments

Comments
 (0)