-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Highlight code blocks, fix emphasis (#712)
* Highlight code blocks, fix emphasis * Fix reviewed changes, fix Markdown emphasis * Fix reviewed changes
- Loading branch information
Showing
1 changed file
with
27 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,27 +44,27 @@ partial interface HTMLTemplateElement { | |
|
||
Concretely, use case (1) is addressed by the component instancing a template as follows: | ||
|
||
``` | ||
``` js | ||
// shadowRoot is the shadow root of a contact card component | ||
shadowRoot.appendChild(template.createInstance()); | ||
``` | ||
|
||
Use case (2) is addressed as follows: | ||
|
||
``` | ||
// Template content is `'<section><h1>{{name}}</h1>Email: <a href="mailto:{{email}}">{{email}}</a></section>'` | ||
``` js | ||
// Template content is `<section><h1>{{name}}</h1>Email: <a href="mailto:{{email}}">{{email}}</a></section>` | ||
shadowRoot.appendChild(template.createInstance({name: "Ryosuke Niwa", email: "[email protected]"})); | ||
``` | ||
|
||
When `createInstance` is called with a JavaScript object, we automatically substitute every mustache syntax with the corresponding value of the property in the object. The resultant DOM would look as though we parsed the following HTML: | ||
|
||
``` | ||
``` html | ||
<section><h1>Ryosuke Niwa</h1>Email: <a href="mailto:[email protected]”>[email protected]</a></section> | ||
``` | ||
For use case (3), `TemplateInstance`'s `update` method can be used as follows: | ||
``` | ||
``` js | ||
let content = template.createInstance({name: "Ryosuke Niwa", email: "[email protected]"}); | ||
shadowRoot.appendChild(content); | ||
... | ||
|
@@ -73,7 +73,7 @@ content.update({name: "Ryosuke Niwa", email: "[email protected]"}); | |
That would update the DOM tree of the template instance to look like: | ||
``` | ||
``` html | ||
<section><h1>Ryosuke Niwa</h1>Email: <a href="mailto:[email protected]”>[email protected]</a></section> | ||
``` | ||
|
||
|
@@ -85,7 +85,7 @@ Because multiple mustache syntaxes within a template work together to absorb var | |
|
||
In order to support use cases (4) and (5), let’s say we have the following `template` element: | ||
|
||
``` | ||
``` html | ||
<template id="foo"><div class="foo {{ f(y) }}">{{ x }} world</div></template> | ||
``` | ||
|
||
|
@@ -95,34 +95,34 @@ For example, suppose a library wanted to provide an ability to auto-linkify a UR | |
|
||
Conceptually we need two objects, say *FY* and *X*, that represent `{{ f(y) }}` and `{{ x }}` which libraries and frameworks can use to read the original expression in each mustache, and use it to update the DOM. We call these objects **template parts**. Template parts should allow the inspection of content of `{{~}}` like so: | ||
|
||
``` | ||
``` js | ||
FY.expression; // Returns "f(y)" | ||
X.expression; // Returns "x". | ||
``` | ||
|
||
Template parts should allow the assignment of a new value after libraries and frameworks evaluated `f(y)` (here, assume `f(y)` evaluates to “bar” and `x` evaluates to “hello”: | ||
|
||
``` | ||
``` js | ||
FY.value = 'bar'; // Equivalent to div.setAttribute('class', 'foo bar'). | ||
X.value = 'hello'; // Equivalent to div.textContent = 'hello world’. | ||
``` | ||
|
||
For template parts which appear as text nodes should also support taking multiple and arbitrary DOM nodes instead of just a text value: | ||
|
||
``` | ||
``` js | ||
// Insert span and a text node in place of {{ x }}. | ||
X.replace([document.createElement('span'), 'hello']); | ||
``` | ||
|
||
Or perhaps we would even want to parse HTML: | ||
|
||
``` | ||
``` js | ||
X.replaceHTML('<b>hello</b>'); | ||
``` | ||
|
||
For use case (5), we need to be able to inspect the attribute name of a template part as in: | ||
|
||
``` | ||
``` js | ||
FY.attributeName; // Returns "class" | ||
``` | ||
|
||
|
@@ -138,15 +138,13 @@ Each template part represents an occurrence of a mustache syntax in the template | |
|
||
Consider, for example, the following template: | ||
|
||
``` | ||
<template type="my-template-type" id="contactTemplate"> | ||
<section><h1>{{name}}</h1>Email: <a href="mailto:{{email}}">{{email}}</a></section> | ||
</template> | ||
``` html | ||
<template type="my-template-type" id="contactTemplate"><section><h1>{{name}}</h1>Email: <a href="mailto:{{email}}">{{email}}</a></section></template> | ||
``` | ||
|
||
That template creates template parts: `NodeTemplatePart` for `{{name}}`, `AttributeTemplatePart` for `{{email}}` in the `href` attribute of the anchor element, and `NodeTemplatePart` for `{{email}}` for the occurrence inside the anchor element. In order to use this template, a template library or the page author would have had to define a `my-template-type` template type; e.g.: | ||
|
||
``` | ||
``` js | ||
document.defineTemplateType('my-template-type', { | ||
processCallback: function (instance, parts, state) { | ||
for (const part of parts) | ||
|
@@ -157,14 +155,14 @@ document.defineTemplateType('my-template-type', { | |
|
||
This template process callback, for illustration purposes, is a simplified version of the **default template process callback**, which is used when the `type` content attribute is omitted on a `template` element. It goes through each template part (i.e., each occurrence of `{{ X }}`) and replaces it with the state object's value looked up by the template part's expression (e.g. `X` for `{{ X }}`). Once defined, this template process callback is invoked whenever the `createInstance` method is invoked on a `template` element of the type `my-template-type`; e.g.: | ||
|
||
``` | ||
``` js | ||
rniwa = {name: "R. Niwa", email: "[email protected]"}; | ||
document.body.appendChild(contactTemplate.createInstance(rniwa)); | ||
``` | ||
|
||
The above code produces the same DOM as the following code under `document.body`: | ||
|
||
``` | ||
``` js | ||
document.body.innerHTML = '<section><h1>R. Niwa</h1>Email:' | ||
+ ' <a href="mailto:[email protected]">[email protected]</a></section>'; | ||
``` | ||
|
@@ -179,7 +177,7 @@ We allow inserting and removing preceding siblings and succeeding siblings in so | |
|
||
Let's suppose we wanted to create a template type which remembers the state object being passed when it was created, and automatically updates the instance whenever property values are changed at some checkpoints (e.g., at the next `requestAnimationFrame`). We can implement this using a template create callback as follows: | ||
|
||
``` | ||
``` js | ||
document.defineTemplateType('self-updating-template', { | ||
createCallback: function (instance, parts, state) { | ||
onCheckPoint(() => instance.update(state)); | ||
|
@@ -246,7 +244,7 @@ In the default template process callback, the fallback or default value of a tem | |
|
||
Note that with this approach, we have an option to address the need to [declaratively instantiate a shadow tree](https://github.com/whatwg/dom/issues/510) by adding a new callback which gets called for each appearance of a template element as follows if we so desired: | ||
|
||
``` | ||
``` html | ||
<template type="shadow-root">~</template> | ||
<script> | ||
document.defineTemplateType("shadow-root", { | ||
|
@@ -269,7 +267,7 @@ With API proposed thus far, conditional statements for use case (8) can be imple | |
|
||
However, this approach won't work for use case (9). To see why, suppose we had the following template: | ||
|
||
``` | ||
``` html | ||
<template type="with-for-each" id="list"> | ||
<ul> | ||
{{foreach items}} | ||
|
@@ -281,7 +279,7 @@ However, this approach won't work for use case (9). To see why, suppose we had t | |
|
||
We can detect `{{foreach items}}` the same way we detect `{{if x}}`, but we there is exactly one template part object for `{{class}}`, `{{value}}`, and `{{label}}`, not per an element in items, and the browser engine doesn't have a way of ignoring out how many parts are needed unless we standardized the exact semantics foreach: | ||
|
||
``` | ||
``` js | ||
document.defineTemplateType('with-for-each', { processCallback: (instance, parts, state) => { | ||
for (const part of parts) { | ||
... | ||
|
@@ -302,7 +300,7 @@ In fact, when `update` method of `TemplateInstance` is subsequently called, we m | |
As suggested by others in the past discussions, we propose to use nested templates for these cases: | ||
``` | ||
``` html | ||
<template type="with-for-each" id="list"> | ||
<ul> | ||
<template directive="foreach" expression="items"> | ||
|
@@ -314,7 +312,7 @@ As suggested by others in the past discussions, we propose to use nested templat | |
In this approach, each inner template appear as its own template part, and the template process callback which supports foreach **directive** would instantiate the inner template as many times as needed as follows: | ||
``` | ||
``` js | ||
document.defineTemplateType("with-for-each", { processCallback: function (instance, parts, state) { | ||
for (const part of parts) { | ||
... | ||
|
@@ -340,7 +338,7 @@ To make this process more streamlined, we propose treating inner template elemen | |
With this interface, the template process callback that implements foreach and other kinds of looping constructs could simply call `replace` with newly constructed elements as follows: | ||
``` | ||
``` js | ||
document.defineTemplateType("with-for-each", { | ||
processCallback: function (instance, parts, state) { | ||
for (const part of parts) { | ||
|
@@ -397,7 +395,7 @@ The `createInstance(optional any state)` method on `HTMLTemplateElement`, when | |
4. Let *parts* be an empty list. | ||
5. For every [descendent](https://dom.spec.whatwg.org/#concept-tree-descendant) node *currentNode* of *instance* in [tree order](https://dom.spec.whatwg.org/#concept-tree-order), run these steps: | ||
1. If *currentNode* is a [template element](https://html.spec.whatwg.org/multipage/scripting.html#the-template-element): | ||
1. Run the concept to _adjust single node case_ with *currentNode***.** | ||
1. Run the concept to _adjust single node case_ with *currentNode*. | ||
2. Let *nodeValueSetter* be a new instance of the _node value setter_ with *currentNode*, the [previous sibling](https://dom.spec.whatwg.org/#concept-tree-next-sibling) of *currentNode*, the [next sibling](https://dom.spec.whatwg.org/#concept-tree-next-sibling) of *currentNode*, an empty _previous replacement nodes_, fully templatized set to the result of running the concept to _determine full templatizability_ with *currentNode*, and an empty _node template part list_. | ||
3. Let *innerPart* be a new instance of `InnerTemplatePart` associated with *currentNode*, an empty _replacement node list_, and *nodeValueSetter*. | ||
4. Append *innerPart* to the end of *parts.* | ||
|
@@ -420,7 +418,7 @@ The `createInstance(optional any state)` method on `HTMLTemplateElement`, when | |
1. Let *value* be *currentNode*'s [data](https://dom.spec.whatwg.org/#concept-cd-data) after [stripping leading and trailing ASCII whitespace](https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace). | ||
2. Let *tokens* to be the result of running the concept to _parse a template string_ on *value*. | ||
3. If *tokens* contains exactly one string, abort the rest of steps and go to the next node. | ||
4. Run the concept to _adjust single node case_ with *currentNode***.** | ||
4. Run the concept to _adjust single node case_ with *currentNode*. | ||
5. Let *nodeValueSetter* be a new instance of the _node value setter_ with the [parent](https://dom.spec.whatwg.org/#concept-tree-parent) [node](https://dom.spec.whatwg.org/#concept-node) of c*urrentNode*, the [previous sibling](https://dom.spec.whatwg.org/#concept-tree-next-sibling) of *currentNode*, an empty _previous replacement nodes_, fully templatized flag set to the result of running the concept to _determine full templatizability_ with *currentNode*, and an empty _node template part list_. | ||
6. For every *token* in *tokens*: | ||
1. If the type of *token* is “string”, | ||
|
@@ -549,7 +547,7 @@ To **apply attribute template part list** with an _attribute value setter_ *attr | |
2. If *partList* contains exactly one _attribute template part_ (this is the fully templatized case): | ||
1. Let *fullTemplate* be the _attribute template part_ in *tokenList*. | ||
2. If the value string of *fullTemplate* is null, [remove an attribute](https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-namespace) with the namespace of the associated attribute of *attributeValueSetter*, the [local name](https://dom.spec.whatwg.org/#concept-attribute-local-name) of the associated attribute of *attributeValueSetter*, and the associated element of *attributeValueSetter.* | ||
3. Otherwise (if the value string of *fullTemplate* is not null*)*, invoke [setAttributeNS](https://dom.spec.whatwg.org/#dom-element-setattributens) with the namespace of the associated attribute of *attributeValueSetter*, the [qualified name](https://dom.spec.whatwg.org/#concept-attribute-qualified-name) of the associated attribute of *attributeValueSetter*, and the value string of *attributeValueSetter* on the associated element of *attributeValueSetter.* | ||
3. Otherwise (if the value string of *fullTemplate* is not null), invoke [setAttributeNS](https://dom.spec.whatwg.org/#dom-element-setattributens) with the namespace of the associated attribute of *attributeValueSetter*, the [qualified name](https://dom.spec.whatwg.org/#concept-attribute-qualified-name) of the associated attribute of *attributeValueSetter*, and the value string of *attributeValueSetter* on the associated element of *attributeValueSetter.* | ||
3. Otherwise: | ||
1. Let *newValue* be an empty string. | ||
2. For each *part* in *partList*: | ||
|