-
Notifications
You must be signed in to change notification settings - Fork 371
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
[dom-parts] Declarative syntax for creating DOM Parts #1003
Comments
Additionally, you mention that the benefit of If someone wanted lightweight nodes wrapping content that they can easily query, that's precisely a You also mention that by only having an opening marker without a closing marker, we would save bytes. It can be hard to speculate on this type of thing because compression algorithms like gzip and brotli make bytes in not equal bytes out, and so repeated content does not always lead to linearly many output bytes. I'd encourage you if you're curious to run a sample piece of HTML with and without end markers into gzip and brotli and see what the size difference actually is. |
as @keithamus stated
That is why there is a need for
|
WCCG had their spring F2F in which this was discussed. Present members of WCCG identified an action item to take the topic of DOM Parts and break it out into extended discussions. You can read the full notes of the discussion (#978 (comment)) in which this was discussed, heading entitled "DOM Parts API". As this issue pertains to DOM parts, I'd like to call out that #999 has been raised for extended discussions and this topic may be discussed during those sessions. |
I'd like to propose an alternative syntax and semantics for DOM Parts that I think would:
The idea is that we would use What I propose, is to pair this with a In Example 1: DSD with Parts that have literally rendered values <template shadowrootmode="open" parsermode="prerender">
This is an {{example}} of how we could use the {{same}} syntax for DSD DOM Parts as {{live}} templates.
<a href="./some/{{link.html}}">We can even use it in attributes.</a>
{{#tag}}
<div>
Nested parts {{work fine}} too!
</div>
{{/tag}}
</template> Example 2: Server rendered parts in the body. <body parsermode="prerender">
This should also work in the {{body}}.
</body> In the above examples, the content between Example 3: Templates with custom bindings
Example 4: Body with custom bindings <body parsermode="custom">
This should also work in the {{this.body}}.
</body> In the above examples, the browser would not render the content between In the future, once the reactivity model, expression syntax, block rendering (conditional, list, etc.) are all worked out, we can add a new I can't claim to have worked out all the edge cases here, but I wanted to drop this in the issue to help move the conversation along more. In our Spring F2F @rniwa thought that there might be a way to support |
Thanks, @tbondwilkinson , for your helpful explanation, and apologies for my lack of clarity. I think my lack of clarity is my bad, as I think both you and @justinfagnani may have read that the same way, which was not my intention. Most especially, thanks for presenting your promising sounding ideas for an important advance forward for the web. Given my track record, I doubt my clarifications below will help, but giving it a college try anyway. Thanks for your patience! I intended: <template prop=name start/> to be shorthand for the proper HTML 5 syntax: <template prop=name start></template> The purpose of the start and end attributes in my attempt for a 1-1 correspondence, was to provide a similar way as the processing instructions would provide as far as indicating where the range started and ended. I didn't intend to imply that the browser currently recognizes those attributes in any way but thanks for the thoughtful tip :-). I was literally proposing a one-for-one swap of processing instruction comments with empty template elements (open/closed tags, with nothing inside, which I was hinting maybe the platform could allow self-closing template tags like it does for div's), Correction/Update: Even the div tag does weird things when trying to make it self closing (to my surprise), so I definitely see how my attempt to make the markup look more palatable caused significant confusion about what I was proposing. Apologies for that. Now if the processing instructions are meant, as part of this proposal, to add any additional information, other than simply saying "behold, a tag that you should turn into a part", like which field name from the host the data is coming from, we could use the full power of HTML and provide that metadata as attributes (if we use self-closing or at least empty template tags with nothing inside in a one-for-one swap with each processing instruction), queryable by developers using css/xslt when hydrating the data out from server rendered content, and possibly other mischief like reconstructing the original template, and search engines could also use that (standardized) notation to help index the content of web pages. Your comment above suggests that it is applicable to your proposal to include metadata in the processing instruction, but as I read more about the proposal, I'm not sure it would be, so maybe I'm reading too much into your proposal? Meaning maybe the advantages I see of using a template element are moot, if we don't include such metadata in the instructions. I guess I'm a little confused about that point. Perhaps none of the uses cases I suggest above have been adopted (or even considered?) by the big frameworks, so I understand that as a result, my suggestion is a non-starter. Again, those use cases are:
Both you (I think) and @justinfagnani have indicated confidence that the cost of a single text node is significantly less than the cost of a single empty / self-closing inert template HTML Element with nothing inside, thus my alternative proposal would impose a heavier load on the browser. That was my suspicion, but a quick performance test I did indicated similar performance metrics, but I'm not enough of an expert (and lack the patience) to do a thorough analysis of the costs. However, I think you and @justinfagnani understand the inner workings of the browser enough to confidently predict that there is a significant performance gap, so I will leave that there. I should note that at various points in the history of the web, there have been introduced tags/attributes that do help provide metadata, which surely comes at some cost to performance, so the question lingers in my mind, even if there is a cost to performance, if those benefits could outweigh the costs, but I'm clearly in a minority of one on this issue, so again, I see why the idea is being dismissed as a non-starter. As for the usefulness of the count, I remember now that my final intentions of using it were to help determine whether some content between an empty template element used to convey an opening section (similar to the opening processing instruction) and the closing open/closed/self-closing template tag indicating where that section ends, if any of the content inside may have "given birth" to addition nodes, meaning a re-running of the binding needs to take place due to a suspicion of staleness. I think if performance is of paramount, and if my suspicions are correct, that even processing instructions impose a small, linear cost, so that the more there are, the worse the performance, that it should be possible to "share" inner processing instructions, so that they serve both as the end of the previous section, and the beginning of the next. Only the first and last nodes of a for-each loop would require beginning (and ending) comment nodes. But again, probably not something the big frameworks have tried, so probably a non-starter to consider (I have no idea what they've tried and found wanting). Getting into the intricacies of how the level could be used is probably not worth the effort to explain, as I think I was addressing use cases that appear to be of little interest to anyone else. |
Not to detract from the exciting proposal @EisenbergEffect has laid out that seems to address all concerns, but also seems to require significant changes to rendering by the browser, I suppose addressing @justinfagnani criticisms of my suggestion of the template element as not being semantically meaningful, I would suggest, as an alternative to processing instructions, we could use the already existing self-closing meta tag, which seems to already be used for search engines quite extensively, and already supports the itemprop attribute (and like processing instructions doesn't render anything). Unfortunately, unlike the template element, the table element spits it out: <table>
<tr>
<meta name="hello">
<td>hello</td>
</tr>
</table> gets rendered as: <meta name="hello">
<table>
<tr>
<td>hello</td>
</tr>
</table> But if we are talking bold actions, maybe this could be changed, to behave more like the template element? |
Having taken some time to reflect on this issue, I realize I was starting to do something I find quite annoying when others do: Critique some request for something other people desperately want, which I have no need or desire to use (I thought I did, which is why I was starting to get involved, but on further reflection, realize I don't). I'm 100% Team Template Instantiation With Built In Support For Moustache Syntax that supports nested loops, conditionals, etc. The sooner the better. I trust the browser vendors can find a way to take server rendered content that is consistent with the output that would be generated by template instantiation, and find the best way, with or without the help of markers, to take that generated content, and apply the template with future updates, in as efficient a way as possible. Perhaps the browser vendors can consult with how frameworks do it for tips, also perfectly fine as far as I'm concerned, especially if the result is better performance / ergonomics, etc. If frameworks don't want to compile their DX optimized syntax to that template syntax (under the hood), that's their prerogative, and if they want to request some other support, also fine, as long as it doesn't block what I want. For my needs / desires:
The only things I think I would ask for is the following, which I'm mulling over before making it a full proposal: A good percentage of websites use microdata . I think nudging developers to make use this feature by making it super-easy, when working with template instantiation, would have beneficial impact for the web and society in general. My (hidden) agenda[TODO]The specific syntax of this proposal is not meant to be read as a particular endorsement of any particular syntax (i.e. handlebar vs moustache), and is up in the air, as far as I know, so please interpret the examples "non-literally". Because there's a performance cost to adding microdata to the output, it should be something that can be opt-in (or opt-out). But basically, for starters, there would be an option we could specify when invoking the Template Instantiation API: emitMicrodata. What this would do: Let's say our (custom element) host object looks like: const host = {
name: 'Bob'
eventDate: new Date()
} And we apply it to the template: <template>
<span>{{name}}</span>
</template> then with the emitMicrodata setting it would generate: <span itemprop=name>Bob</span> FormattingIf template instantiation supports formatting: <template>
<time>{{eventDate.toLocaleDate|ar-EG, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }}}</time>
<template> it would generate: <time itemprop=eventDate datetime=2011-11-18T14:54:39.929Z>11/18/2011</time> Now let's talk about the dreaded interpolation scenario. <template>
<div>Hello {{name}}, the event will begin at {{eventDate}}</div>
</template> Template instantiation would generate: <div>Hello <meta itemprop=name/>Bob<meta content>, the event will begin at <meta itemprop=eventDate itemtype=date content=2011-11-18T14:54:39.929Z>11/18/2011</div> |
This is my (evolving) proposal. |
My proposal is now quite different. Please give another look, I think this getting closer to what we want (in my opinion, obviously). |
There's an unfortunate stipulation I had missed in my proposal: According to MDN:
Personally, I think this should be lifted (thinking about primitive types), but that's an uphill battle. So I've updated the proposal so that template instantiation would only dynamically set itemprop, itemref and meta tags with content, and not emit any itemtypes whatsoever, leaving that part up to the developer. The proposal is extremely light now, and I recognize it could be done via tooling, that would compile to the template syntax, whatever that syntax is, if there are any objections to it. But I do think it is possible, based on what I know, to fully define a hierarchical tree beyond what the HTML structure provides, with the help of meta tags, and itemref/itemscope and id's, without needing to introduce yet more new syntax in the HTML (processing instructions), and without violating proper HTML decorum. And sticking with moustache syntax. Please let me know if I'm missing anything. |
One serious shortcoming where microdata falls short as a replacement for processing instructions is in specifying attributes that we want to turn into a part. Maybe, if the performance is significantly improved by using processing instructions for this, it could be an interim solution. I kind of find it difficult to picture servers emitting these processing instructions long term just for that, and seems like one of those things that would get introduced and rarely used and quickly forgotten. But the limitation is really a significant shortcoming, in my view, of the microdata standard. Much valuable information that would be useful for search engines (and hydrating) is contained in aria- attributes as well as such as attributes like title, value, and alt. This one would require a new global microdata attribute: <img itempropmap="photoCaption: alt; photoDescription: title; photographer: aria-label"
alt="Bear rummaging through garbage"
aria-label="Photo by Simone de Beauvoir"
title="Click on photo for more info" > |
I've been using <x-card>
<h1 x-if={!!firstName}>{firstName}</h1>
<x-progress x-if={!ready} value={expressions:computedStatus}></x-progress>
</x-card> Restructured I know I've noticed I've been "polluting" my prototype with expressions ( The resulting syntax would be |
Branched from #999
@bahrus
<template>
is more oriented towards populating a piece of HTML that can be later cloned by JavaScript. It does not seem fit to indicate nodes of interest.There are few concerns with using any sort of Node (
<template>
or otherwise) to mark ranges. Using a new Node, and nesting child nodes in that node, modifies the DOM structure in more meaningful ways than adding PI/comments. If the node(s) of interest are now children of some other node, they are no longer children of their parent in the desired rendered output. Imagine a list where only one child is dynamic - if that child and no others are nested in another Node, your CSS selectors become quite a bit more difficult to implement. Today, applications do not nest their dynamic content in special nodes, so introducing a new node and nesting any dynamic content is probably a non-starter for most applications, which makes it a non-starter.My general concern with
<template>
in particular is that although in this case we're using the same word, "template" in<template>
means a template of HTML that can be cloned and updated. "template" in DOM parts refers to a user-authored template that has dynamic content. I would hesitate to represent a dynamic content point with<template>
, as it just doesn't feel like a good fit for that language feature.Other comments below.
A.
Hello, <?child-node name?>Adam<?/child-node?>
With the minor nit that processing instructions are not HTML elements and so therefore do not have end tags like a normal element would, and also do not have attributes like a normal element would, yes this is the current proposal. I've slightly corrected your example.
B.
Hello <template prop=name start/>Adam<template end/>
FYI the syntax for ending
<template>
nodes is</template>
not<template end/>
.<template>
elements do not render in the DOM initially, they require JavaScript to clone and insert into the DOM, see the example here. So by using a<template>
, you wouldn't render "Adam". If you wanted to render "Adam", or some other dynamic value in that position, in your case you would need to locate the<template>
and then prepend or append the content before or after the template. That seems like an odd API, and prevent server-side rendered content from being rendered until JavaScript has had the chance to do that hydration, which isn't ideal. If the browser would render "Adam", that of course breaks the<template>
spec. If we make<template>
have a new mode where "Adam" is rendered... why would we use<template>
at all in that case.Caveat that declarative shadow DOM does declaratively insert
<template>
and its content, but that's probably a tangent.C.
Hello <template prop=name node-count=1 level=0/>Adam
<template>
nodes need to be closed, so I'm not sure how you're imagining this would work.D.
Hello <?child-node prop=name node-count=1 level=0 ?>Adam
This is in theory possible but requires a lot of book keeping to continually update the
node-count
attribute after any content change. You can imagine examples where a range is replaced by a series of nodes, rather than just one. I'm not sure what "level" here indicates.The text was updated successfully, but these errors were encountered: