Add <template for> for declarative out-of-order streaming#11818
Add <template for> for declarative out-of-order streaming#11818foolip wants to merge 31 commits into
Conversation
|
I've made some additional changes but things don't quite make sense yet. The direction I'm heading in is:
There are options for where to store the bookkeeping. I initially put it on the |
It also needs to fail if that first element is no longer a child of the target.
Yea makes sense, that way you don't have to deal with grandparents. |
|
I've now rewritten a lot of this to make it match my previous comment, and I think it's in good enough shape for review now. I left two inline issues:
and
@noamr for the first one, you said we should fail, but what would that mean? |
It means no further content is prepended. |
|
Okay, should that state be sticky, or what happens if the nodes later realign so that the check passes? |
Yea I think it errors the whole thing |
|
Missing pieces following the TPAC session:
|
|
I've given some thoughts to error handling for <!doctype html>
<body>
<div contentname=foo><span id=refnode>will be removed</span></div>
<template contentmethod=prepend>
<div contentname=foo>
<p>this element is inserted</p>
<!-- the script removes the "content target first child" node -->
<script>window.refnode = document.getElementById('refnode'); refnode.remove();</script>
<p>reference node is gone, can this element be inserted?</p>
<!-- put the reference node back -->
<script>document.querySelector('[contentname=foo]').appendChild(refnode);</script>
<p>is this OK because the reference node is back?</p>
</div>
<div contentname=foo>
<p>is this fine because we saved a new reference node?</p>
</div>
</template>Options on what constitutes an error:
Options on handling:
Options on reporting:
It looks like the parser currently never fires events while it's still running, the only events are "DOMContentLoaded" and "load". Anything else that seems be fired by parsing is actually triggered by some other algorithm run as a side effect of what the parser does. I don't think that parse errors are a good fit either, because the error can't easily be expressed in terms of the input, but the shape of the DOM tree. My thinking now is that we should instead guarantee that the nodes are inserted, that we don't discard node midstream. For |
This is controlled by https://dom.spec.whatwg.org/#concept-document-allow-declarative-shadow-roots. We should probably rename this to cover both of these behaviors. |
af700a0 to
1e56dfb
Compare
|
I've pushed a change to rewrite this as This depends on three other PRs: |
| <p>If <var>scope</var> is a <code>template</code> element, then set <var>scope</var> to | ||
| <var>scope</var>'s <span>template contents</span>.</p> | ||
|
|
||
| <p class="note">This is to support patching inside <code>template</code> elements.</p> |
There was a problem hiding this comment.
| <p class="note">This is to support patching inside <code>template</code> elements.</p> | |
| <p class="note">This is to support patching inside <code>template</code> elements. It | |
| would work for normal templates or declarative shadow roots, however it effectively disallows | |
| nested patches, as the <span>template contents</span> is separate from the <span>insertion | |
| target</span></p> |
| <li><p>Let <var>nextNode</var> be <var>currentNode</var>'s | ||
| <span>next sibling</span>.</p></li> | ||
|
|
||
| <li><p><span data-x="concept-node-remove">Remove</span> <var>currentNode</var>.</p></li> |
There was a problem hiding this comment.
This can have impllications if the node is an iframe with a pagehide, e.g. it can synchronously add more siblings or change things about the document. It's perhaps OK but an alternative would be to accumulate all the nodes before the <?end> and then remove them in one go.
There was a problem hiding this comment.
How would you remove them all at once? That's not a primitive that we have. How do we account for changes here?
There was a problem hiding this comment.
Changes in there won't have any effect. You'd have accumulated a list of nodes in this step, and then when we see <?end> we remove all of those one by one. If there is a pagehide or whatnot for one of them it wouldn't be able to change that list.
There was a problem hiding this comment.
That is probably a good idea.
We probably still need to add guards though because concept-node-remove for instance asserts node's parent is non-null.
There was a problem hiding this comment.
Right, those should generally behave like Node.prototype.remove()
| [<span>HTMLConstructor</span>] constructor(); | ||
|
|
||
| readonly attribute <span>DocumentFragment</span> <span data-x="dom-template-content">content</span>; | ||
| [<span>CEReactions</span>, <span data-x="xattr-Reflect">Reflect</span>="<span data-x="attr-template-for">for</span>"] attribute DOMString <dfn attribute for="HTMLTemplateElement" data-x="dom-template-htmlFor">htmlFor</dfn>; |
There was a problem hiding this comment.
Why do we use htmlFor here? I thought that was no longer needed given changes to ECMAScript? We should use for.
There was a problem hiding this comment.
OTOH, do we really want the naming convention to be different for the same-named attribute on different HTML elements?
There was a problem hiding this comment.
I guess it depends on if we try to proceed with #9379 for the other cases.
There was a problem hiding this comment.
I think at this point it would be preferable to expose it as htmlFor and have aliases for all of them when we decide on #9379 rather than end up with a mix and match of for and htmlFor.
There was a problem hiding this comment.
I guess I don't feel super strongly myself, but web developers really seem to dislike the prefix so I'm somewhat hesitant to spread it further.
There was a problem hiding this comment.
I wonder if @jakearchibald or @tunetheweb have a strong opinion about this. My opinion right now is that we should fix all of these together but I could be convinced that drawing the line here makes more sense.
There was a problem hiding this comment.
No strong opinion myself but do agree that it seems weird to fix this one (and not support htmlFor at all?) while htmlFor is still the "standard" way IIUC?
There was a problem hiding this comment.
Suggesting to resolve this comment for now. We are likely to get mixed opinions from developers. Let's create aliases for this together with #9379.
There was a problem hiding this comment.
Discussed this with @jakearchibald on #9379 as well. There is rough consensus on the plan of starting with htmlFor and aliasing all of those attributes to for (+ class) when we resolve on that.
| <th> <code data-x="">for</code> | ||
| <td> <code data-x="attr-template-for">template</code> | ||
| <td> Updates existing content | ||
| <td> Text |
There was a problem hiding this comment.
It looks like we're missing an update to the template element index entry.
| <li><p>Let <var>nextNode</var> be <var>currentNode</var>'s | ||
| <span>next sibling</span>.</p></li> | ||
|
|
||
| <li><p><span data-x="concept-node-remove">Remove</span> <var>currentNode</var>.</p></li> |
There was a problem hiding this comment.
How would you remove them all at once? That's not a primitive that we have. How do we account for changes here?
|
|
||
| <ol> | ||
| <li><p>If <var>sibling</var> is not a <code>ProcessingInstruction</code> node, then | ||
| <span>continue</span>.</p></li> |
There was a problem hiding this comment.
So now we are doing nested traversals. I'm not sure that actually works as the descendant iterator will also just pick the next one. Presumably we want to skip over the descendants we are handling here?
There was a problem hiding this comment.
This is not exactly "nested traversal". We reach the first matching PI and then sibling-traverse it, but after that we're done.
I double-checked this algorithm and I think it does the right thing... Can you be more specific if there is an issue here?
| <p class="note">This is to support patching inside plain <code>template</code> elements or | ||
| those using <code data-x="attr-template-shadowrootmode">shadowrootmode</code>. Nested | ||
| patching is not supported, since in this case <var>scope</var>'s <span>template | ||
| contents</span> will be empty and <span>prepare content patching</span> will fail.</p> |
There was a problem hiding this comment.
"Plain" template contents are a DocumentFragment. This means you have a bunch of type errors as you exclude that earlier.
| <p class="note">This is to support patching <code>head</code> with a <code>template</code> | ||
| inside <code>body</code>.</p> | ||
| </li> | ||
|
|
There was a problem hiding this comment.
We should have an assert here that scope is an Element or DocumentFragment.
Though also, can "the body element" be made to return null above with script?
There was a problem hiding this comment.
If the document doesn't have a body then the condition after "otherwise" is never true. I think we can assert that the scope is an element or document fragment.
Fixes #11542.
(See WHATWG Working Mode: Changes for more details.)
/indices.html ( diff )
/infrastructure.html ( diff )
/parsing.html ( diff )
/scripting.html ( diff )
/timers-and-user-prompts.html ( diff )