diff --git a/proposals/DOM-Parts-Declarative-Template.md b/proposals/DOM-Parts-Declarative-Template.md deleted file mode 100644 index 2d6121f0..00000000 --- a/proposals/DOM-Parts-Declarative-Template.md +++ /dev/null @@ -1,73 +0,0 @@ -# DOM Parts Declarative Template API - -This proposal covers the declarative API for creating a DOM part within a -`<template>` element. - -## Proposal - -Double curly braces `{{}}` provide markers for DOM parts. - -In the most basic level, this proposal can produce the three DOM parts: -`NodePart`, `AttributePart`, `ChildNodePart`. - -### Basic Examples - -Suppose we had the following template in some HTML extension template languages -where `{name}` and `{email}` indicated locations of dynamic data insertion: - -```html -<section> - <h1 id="name">{name}</h1> - Email: <a id="link" href="mailto:{email}">{email}</a> -</section> -``` - -And the application has produce a template with the following content: - -```html -<template> - <section> - <h1 id="name">{{}}</h1> - Email: <a id="link" href="{{}}">{{}}</a> - </section> -</template> -``` - -This will create a `ChildNodePart` attached to `<h1>` with no content, a -`NodePart` attached to `<a>`, and an `AttributePart` connected to `href`. - -A framework could fetch these parts use either a -[`DocumentPart`](./DOM-Parts-Imperative.md#option-2-documentpartgroup) on the -`DocumentFragment` produced by the template. - -## `PartialAttributePart` - -Allowing `{{}}` inside an attribute with some static content raises the same -question as a -[`PartialAttributePart`](./DOM-Parts-Imperative.md#partial-attribute-updates), -but if the imperative questions are resolved so too could the declarative -template syntax. - -## Metadata - -Templating systems may need to serialize data about the nodes they are marking -into the processing instructions. Or at the very least parts could be named so -that they are easier to fetch. - -```html -<div>{{email data="foo"}}</div> -``` - -This could be exposed on the imperative API to be consumed in JavaScript by -application logic. - -## Choice of marker - -The `{{}}` is a reasonable DOM part marker, but it could be something else, or -it could even be something that is declared as the placeholder. - -## Compatability - -It may be challenging to implement `<template>` parsing of `{{}}` for -compatability reasons, so there may need to be restrictions such as only -allowing this template syntax in a new API like `createTemplateWithParts()`. diff --git a/proposals/DOM-Parts-Declarative.md b/proposals/DOM-Parts-Declarative.md new file mode 100644 index 00000000..f9ab5088 --- /dev/null +++ b/proposals/DOM-Parts-Declarative.md @@ -0,0 +1,131 @@ +# DOM Parts Declarative API + +This proposal covers the declarative API for creating a DOM part within a +`<template>` element and main document HTML. + +## Proposal + +Double curly braces `{{}}` provide markers for DOM parts. + +In the most basic level, this proposal can produce the three DOM parts: +`NodePart`, `AttributePart`, `ChildNodePart`. + +### Basic Examples + +Suppose we had the following template in some HTML extension template languages +where `{name}` and `{email}` indicated locations of dynamic data insertion: + +```html +<section> + <h1 id="name">{name}</h1> + Email: <a id="link" href="mailto:{email}">{email}</a> +</section> +``` + +And the application has produced an HTML `<template>` with the following +content: + +```html +<template> + <section> + <h1 id="name">{{}}</h1> + Email: <a id="link" href="{{}}">{{}}</a> + </section> +</template> +``` + +This will create a `ChildNodePart` attached to `<h1>` with no content, an +`AttributePart` connected to `href`, and a `ChildNodePart` connected to `<a>` +with no content. + +A framework could fetch these parts using the `getPartRoot()` on the +[`DocumentFragment`](./DOM-Parts-Imperative.md#retrieving-a-documentpartroot) +and then calling [`getParts()`](./DOM-Parts-Imperative.md#getparts). + +## Enablement + +For any DOM node, including `<template>`, a new `parseparts` attribute is +introduced that indicates to the parser it should parse DOM part tags as DOM +parts. + +```html +<div parseparts></div> +``` + +Even for `innerHTML` use cases, only DOM nodes that are wrapped DOM with the +`parseparts` attribute will use declarative parts. + +## Node parts + +For node parts, a `{{}}` tag could be provided as an attribute. + +```html +<template> + <section {{}}></section> +</template> +``` + +Would create a `NodePart` for `<section>`. + +## Partial attributes + +Allowing `{{}}` inside an attribute works the same as a +[partial attribute update](./DOM-Parts-Imperative.md#partial-attribute-updates), +in that it will create an `AttributePart` for the entire attribute, but it will +have multi-valued `value` property. + +```html +<template> + <section> + <h1 id="name">{{}}</h1> + Email: <a id="link" href="mailto:{{}}">{{}}</a> + </section> +</template> +``` + +The `AttributePart` for `href` would have `statics` equal to `['mailto:', '']`. +Empty string values are provided for any markers without default content. + +## Default Values + +To provide default values for any part, a part can be split into a start `{{#}}` +and finish `{{/}}` indicators. + +```html +<template> + <section> + <h1 id="name">{{#}}Ryosuke Niwa{{/}}</h1> + Email: + <a id="link" href="mailto:{{#}}rniwa@apple.com{{/}}"> + {{#}}rniwa@apple.com{{/}} + </a> + </section> +</template> +``` + +## Names and Metadata + +Templating systems may need to serialize data about the nodes they are marking +into the processing instructions. Or at the very least parts could be named so +that they are easier to fetch. + +```html +<div>{{email data="foo"}}</div> +``` + +This could be exposed on the imperative API to be consumed in JavaScript by +application logic. + +For parts that are split between opening and closing, it's possible to have +multiple metadata values which are included as two elements in the `metadata` +field. + +``` +{{# metadata1}}default value{{/ metadata2}} +``` + +## Choice of marker + +The `{{}}` and `{{#}}{{/}}` are reasonable DOM part markers, but this is open to +proposals. It's possible to even allow the page to decide ewhat their markers +should be. diff --git a/proposals/DOM-Parts-Imperative.md b/proposals/DOM-Parts-Imperative.md index 8034a6c9..f8aee1c0 100644 --- a/proposals/DOM-Parts-Imperative.md +++ b/proposals/DOM-Parts-Imperative.md @@ -8,35 +8,63 @@ A DOM part is represented by the `Part` interface and its sub interfaces. ```webidl interface Part { - attribute any value; - void commit(); + attribute any value; + + readonly attribute PartRoot root; + readonly attribute FrozenArray<DOMString> metadata; + void commit(); +}; + +dictionary PartInit { + FrozenArray<DOMString> metadata; }; interface NodePart : Part { - constructor(Node node); - readonly attribute Node node; + constructor(PartRoot root, Node node, optional PartInit init = {}); + + readonly attribute Node node; }; interface AttributePart : Part { - constructor(Element element, DOMString qualifiedName, DOMString? namespace); - readonly attribute DOMString prefix; - readonly attribute DOMString localName; - readonly attribute DOMString namespaceURI; + constructor( + PartRoot root, + Element element, + DOMString qualifiedName, + optional DOMString? namespace = null, + optional Array<DOMString> statics = [], + optional PartInit init = {}); + + readonly attribute DOMString prefix; + readonly attribute DOMString localName; + readonly attribute DOMString namespaceURI; + readonly attribute DOMString rawName; + + readonly attribute FrozenArray<DOMString> statics; }; interface ChildNodePart : Part { - constructor(Node node, Node? previousSibling, Node? nextSibling); - readonly attribute Node? previousSibling; - readonly attribute Node? nextSibling; + constructor( + PartRoot root, + optional Node? previousSibling = null, + optional Node? nextSibling = null, + optional PartInit init = {}); + + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute FrozenArray<Node> children; +}; + +interface DocumentPartRoot { }; ``` -The `Part` has one property named "value" of -[_any_ type](https://heycam.github.io/webidl/#idl-any). +`Part` points to a specific type of DOM, most commonly a `Node` or collection of +sibling nodes. Depending on the type of `Part`, it has various properties that +expose more information. -When a new value is set or assigned to a DOM part, the change does not -immediately reflect back to the corresponding -[node](https://dom.spec.whatwg.org/#concept-node), its +Each `Part` has a `value` that can be updated. When a new value is set or +assigned to a DOM part, the change does not immediately reflect back to the +corresponding [node](https://dom.spec.whatwg.org/#concept-node), its [attributes](https://dom.spec.whatwg.org/#concept-attribute), or its [properties](<(https://tc39.es/ecma262/#sec-object-type)>). Instead, the new value is staged to be later committed in a batch. This batching reduces the @@ -44,7 +72,7 @@ runtime overhead of constantly returning control back from browser's implementation to JavaScript between each DOM mutation and allows browser engine's to avoid or batch certain sanity checks and housekeeping tasks. -In the most basic level, this proposal consists of three DOM parts: +In the most basic level, this proposal consists of four DOM parts: 1. `NodePart` represents a single [node](https://dom.spec.whatwg.org/#concept-node). @@ -54,8 +82,11 @@ In the most basic level, this proposal consists of three DOM parts: [child](https://dom.spec.whatwg.org/#concept-tree-child) [nodes](https://dom.spec.whatwg.org/#concept-node) of a node which can be [replaced](https://dom.spec.whatwg.org/#concept-node-replace). +1. `DocumentPartRoot` represents a + [document](https://dom.spec.whatwg.org/#concept-document) or + [document fragment](https://dom.spec.whatwg.org/#interface-documentfragment). -### Basic Examples +### Basic examples Suppose we had the following template in some HTML extension template languages where `{name}` and `{email}` indicated locations of dynamic data insertion: @@ -87,9 +118,13 @@ as follows: ```js const name = staticContent.getElementById("name"); const link = staticContent.getElementById("link"); -const namePart = new ChildNodePart(name); -const emailPart = new ChildNodePart(link); -const emailAttributePart = new AttributePart(link, "href"); +const namePart = new ChildNodePart(document.getPartRoot(), name); +const emailPart = new ChildNodePart(document.getPartRoot(), link); +const emailAttributePart = new AttributePart( + document.getPartRoot(), + link, + "href" +); ``` Then assigning values as follows will update the DOM: @@ -112,141 +147,66 @@ The resultant DOM will look like this: </section> ``` -## Part Groups - -DOM parts need grouping and ownership to provide batching and to enable parts -created declaratively to be retrieved and updated by JavaScript. - -### Option 1. `PartGroup` - -One option is to make the concept of DOM part group a real DOM -[interface](https://heycam.github.io/webidl/#idl-interfaces): - -```webidl -interface PartGroup { - constructor(sequence<Part> parts); - readonly attribute FrozenArray<Part> parts; - void commit(); -} -``` - -Note that if we allow a single `Part` to belong to multiple `PartGroup`s, the -first `PartGroup` which commits the changes would apply the mutations. In -effect, this allows -non-[partitioned](https://en.wikipedia.org/wiki/Partition_of_a_set) grouping of -`Part` objects to be committed together. - -There is also a question of how mutable parts should be, and whether a -`PartGroup` can appear as a part of another `PartGroup` for nested template -instances or not. It doesn't make much sense for the list of _DOM parts_ -associated with `PartGroup` to get mutated after we've started committing things -but there certainly is a room for adding or removing _DOM parts_ based on new -input or state. - -If we made the relationship between DOM parts and `PartGroup` not dynamically -mutable, users of this API could still create a new `PartGroup` each time such a -mutation would have needed instead. - -The `parts` order would be the order in which DOM part was inserted to the -`PartGroup`. Although there could be multiple DOM parts which reference the same -element in different parts of the array, that doesn't necessarily pose an -obvious issue other than a slight inefficiency in batching certain DOM -operations. - -### Option 2. `DocumentPartGroup` +## Retrieving a `DocumentPartRoot` -A dynamic list of parts could be maintained at the `document` (and document -fragment) level that would allow fetching all parts. +For every document or document fragment, a new method `getPartRoot()` is added +that returns a `DocumentPartRoot`. ```webidl -interface DocumentPartGroup { - readonly attribute Array<Part> parts; - void commit(); -} - partial interface Document { - readonly attribute DocumentPartGroup documentPart; + DocumentPartRoot getPartRoot(); } -``` - -The list of parts would be cached initially on render, and then invalidated for -lazy recalculation for any new `Part` that was declaratively or imperatively -added to the `document`. - -The `parts` array would be in DOM-order. The exact algorithm for how to keep the -`parts` array up to date with the `document` is an open question. - -### Option 3. `ChildNodePart` is a `PartGroup` -To make `DocumentPartGroup` more performant and to provide better structure to -`Part` relationships for a more optimal DOM walk, `ChildNodePart` could itself -be a `PartGroup`, and would contain any `Part` objects that were nested inside -its range, and child `parts` would not be part of any parent `ChildNodePart` or -`DocumentPartGroup`. - -```webidl -interface ChildNodePart { - readonly attribute Array<Part> parts; - void commit(); +partial interface DocumentFragment { + DocumentPartRoot getPartRoot(); } ``` -The `parts` array would be in DOM-order. The exact algorithm for how to keep the -`parts` array up to date with the DOM subtree rooted by the `ChildNodePart` is -an open question. +## Nested parts and cloning with `PartRoot` -## Cloning Parts +`DocumentPartRoot` and `ChildNodePart` both implement `PartRoot` and contain +nested parts. -Cloning parts along with the nodes they refer to is a major use case for DOM -parts. +``` +interface mixin PartRootMixin { + FrozenArray<Part> getParts(); + PartRoot clone(); +}; -### Option 1: `cloneWithParts` +ChildNodePart includes PartRootMixin; +DocumentPartRoot includes PartRootMixin; -One option would to add a new API to `Node`: +typedef (DocumentPartRoot or ChildNodePart) PartRoot; +``` -```webidl -partial interface Node { - NodeWithParts cloneWithParts(optional CloneOptions options = {}); -}; +### `getParts()` -dictionary CloneOptions { - boolean deep = true; - Document? document; - PartGroup? partGroup; -}; +`getParts()` returns an array of parts that are contained within a `PartRoot`. -dictionary NodeWithParts { - Node node; - PartGroup? partGroup; -}; -``` +The array of parts would be cached initially on render, and then invalidated for +lazy recalculation for any new `Part` that was declaratively or imperatively +added to the `document` or for DOM node additions or removals. -Here, we're proposing a slightly nicer API by combining -[`importNode`](https://dom.spec.whatwg.org/#dom-document-importnode) and -[`cloneNode`](https://dom.spec.whatwg.org/#dom-node-clonenode) and making the -[cloning](https://dom.spec.whatwg.org/#concept-node-clone) deep by default. +The array of parts would be in DOM-order. The exact algorithm for how to keep +the array up to date with the `document` is up to the user-agent, but is +invalidated anytime the user-agent detects changes to the DOM that could +invalidate the array. -### Option 2: `cloneWithParts` on `DocumentPart` and `ChildNodePart` +#### Open nested part questions -Since `DocumentPart` and `ChildNodePart` both are rooted at a specific node, the -clone semantics are clearer: +The precise specification of invalidation, caching, etc. is still yet to be +described. -```webidl -partial interface DocumentPart { - NodeWithParts clone(); -} +### `clone()` -partial interface ChildNodePart { - NodeWithParts clone(); -} -``` +`clone()` deep clones the `PartRoot`, its nested parts, and the DOM nodes. It +returns a new `PartRoot` of the same type as the callee. -### Other Cloning Questions +#### Open cloning questions -There are some questions here as well. What happens to the current values of DOM -parts? Do we allow DOM parts to have some non-initial values and do we clone -those values as well? If so, what do we do with proposed extensions like -`PropertyPart` / `CustomPart`? +There are some edge cases that are worth thinking about, like whether +un-committed values of parts are cloned or whether invalid parts should be +cloned. ## Partial Attribute Updates @@ -256,156 +216,87 @@ had initially contained `mailto:` before `{email}` but we could not capture this prefix in the attribute value because `AttributePart` could only set the whole attribute value. -There are a few options for how to support the use case of updating attributes -with embedded static content such as `mailto:` - -### Option 1. Create Multiple `AttributePart`s Together - -In this approach, `AttributePart` gets a new static function which creates a -list of `AttributePart`s which work together to set a value when the values are -to be committed: +Instead of `AttributePart` having a single string `value`, it could optionally +take an `Array` that contains values that should be concatenated together. This +allows updating individually parts of the attribute without needing to serialize +the entire string. ```js -const [firstName, lastName] = AttributePart.create(element, "title", null, [ - null, - " ", - null, -]); -// Syntax to be improved. Here, a new AttributePart is created between each string. +const part = AttributePart(document.getPartRoot(), element, "href"); +part.value = ["mailto: ", email]; ``` -- **Pros**: Simplicity. -- **Cons**: Coming up with a nice syntax to create a sequence of AttributePart - and string can be tricky. - -### Option 2. Introduce `AttributePartGroup` - -In this approach, we group multiple `AttributePart`s together by creating an -explicit group: +Additionally, `AttributePart` can be constructed with a list of static parts. If +there are any static parts, the first static part will precede the first value, +the second static part will precede the second value, and so on until the final static value is reached. ```js -const firstName = new AttributePart(); -const lastName = new AttributePart(); -const group = AttributePartGroup(element, "title"); -group.append(firstName, " ", lastName); -``` - -This is morally equivalent to option 1 except there is an explicit grouping -step. - -- **Pros**: Nicer syntax by the virtue of individual "partial" `AttributePart`'s - existence at the time of grouping. Code that assigns values to `AttributePart` - only needs to know about `AttributePart` -- **Cons**: More objects / complexity. `AttributePart` will have two modes. - -### Option 3. Introduce `AttributePartFragment` - -Unlike option 2, this creates `AttributePartFragment`s from `AttributePart`, -meaning that `AttributePart` in option 3 plays the role of `AttributePartGroup` -in option 2: - -```js -const firstNamePartial = new AttributePartFragment(); -const lastNamePartial = new AttributePartFragment(); -const part = AttributePart(element, "title"); -part.values = [firstNamePartial, " ", lastNamePartial]; +const part = AttributePart(document.getPartRoot(), element, "href", undefined, [ + "mailto: ", +]); +part.value = email; ``` -- **Pros**: Nicer syntax by the virtue of individual `AttributePartFragment`'s - existence at the time of grouping. `AttributePart` just knows one thing to do: - to set the whole content attribute value. -- **Cons**: More objects / complexity. Code that uses a template has to deal - with two different kinds of objects: `AttributePartFragment` and - `AttributePart`. - -### Option 4. Support arbitrary JavaScript objects - -One way of punting is to support arbitrary JavaScript objects as `value` that -conform to some interface. This interface could be as simple as `toString()`, or -could use `Symbol` to determine how to populate attributes. This would allow -code that wanted to represent partial attributes, but would maintain the -property that parts represent nodes or groupings of nodes. +This API may be strengthened further to take in some parsed template string, +which would allow the browser to determine which parts of the attribute were +compile-time constants. This could allow using tagged template literals to pass +static content to the browser. ```js -class TitleAttributeValue { - constructor() { - this.firstName = ""; - this.lastName = ""; - } - - toString() { - return `${this.firstName} ${this.lastName}`; - } -} - -const part = AttributePart(element, "title"); -part.value = new TitleAttributeValue(); -part.value.firstName = "Ryosuke"; -part.value.lastName = "Niwa"; +const part = AttributePart( + document.getPartRoot(), + element, + "href", + undefined, + attribute`mailto: ${0}` +); +part.value = [email]; ``` -- **Pros** No need for new `PartialAttributePart` or `AttributePart` - coordination. Does not block a future API object that represents partial - attributes. -- **Cons** Need a way to represent partial attributes that are declaratively - defined. +At this time there is no specific native representation of templates that could +be inspected to determine with certainty which strings were compile time +constants, but if some a representation were to exist, this API could provide +better security for attributes that were sens ## Sibling `ChildNodePart`s Like partial attribute updates, when there are multiple points of interests under a single [parent](https://dom.spec.whatwg.org/#concept-tree-parent) [node](https://dom.spec.whatwg.org/#concept-node), and they're next to each -other, index does not adequately describe a specific location in the DOM when -other parts [insert](https://dom.spec.whatwg.org/#concept-node-insert) or +other, previous and next sibling does not adequately describe a specific +location in the DOM when other parts +[insert](https://dom.spec.whatwg.org/#concept-node-insert) or [remove](https://dom.spec.whatwg.org/#concept-node-remove) [children](https://dom.spec.whatwg.org/#concept-tree-child). -### Option 1. Create Multiple `ChildNodePart`s Together +### Option 1. Create multiple `ChildNodePart`s together In this approach, `ChildNodePart` gets a new static function which creates a list of `ChildNodePart`s which work together to set a value when the values are to be committed: ```js -const [firstName, lastName] = ChildNodePart.create(element, null, null, [ +const [firstName, lastName] = ChildNodePart.create( + document.getPartRoot(), + element, null, - " ", null, -]); + [null, " ", null] +); ``` - **Pros**: Simplicity. - **Cons**: Coming up with a nice syntax to create a sequence of `ChildNodePart` and string can be tricky. -### Option 2. Introduce `ChildNodePartGroup` - -In this approach, we group multiple `ChildNodePart`s together by creating an -explicit group: - -```js -const firstName = new ChildNodePart(); -const lastName = new ChildNodePart(); -const group = ChildNodePartGroup(element, null, null); -group.append(firstName, " ", lastName); -``` - -This is morally equivalent to option 1 except there is an explicit grouping -step. - -- **Pros**: Nicer syntax by the virtue of individual "partial" `ChildNodePart`s - existence at the time of grouping. Code that sets new children to - `ChildNodePart`s only needs to know about `ChildNodePart`. -- **Cons**: More objects / complexity. `ChildNodePart` will have two modes. - -### Option 3. Introduce `PartialChildNodePart` +### Option 2. Introduce `PartialChildNodePart` This creates `PartialChildNodePart` from `ChildNodePart`: ```js const firstNamePartial = new PartialChildNodePart(); const lastNamePartial = new PartialChildNodePart(); -const part = ChildNodePart(element, null, null); +const part = ChildNodePart(document.getPartRoot(), element, null, null); part.values = [firstNamePartial, " ", lastNamePartial]; ``` @@ -415,17 +306,22 @@ part.values = [firstNamePartial, " ", lastNamePartial]; with two different kinds of objects: `PartialChildNodePart` and `ChildNodePart`. -### Option 4. Allow `nextSibling` and `previousSibling` to point to another `ChildNodePart` +### Option 3. Allow `nextSibling` and `previousSibling` to point to another `ChildNodePart` -We would update `ChildNodPart` interface as follows and allow `previousSibling` -and `nextSibling` to point to another `ChildNodePart` as well as `Node`: +Update `ChildNodPart` interface as follows and allow `previousSibling` and +`nextSibling` to point to another `ChildNodePart` as well as `Node`: ```webidl interface ChildNodePart : Part { - constructor(Node node, (Node or ChildNodePart)? previousSibling, (Node or ChildNodePart)? nextSibling); - readonly attribute Node parentNode; - readonly attribute (Node or ChildNodePart)? previousSibling; - readonly attribute (Node or ChildNodePart)? nextSibling; + constructor( + PartRoot root, + optional (Node or ChildNodePart)? previousSibling = null, + optional (Node or ChildNodePart)? nextSibling = null, + optional PartInit init = {}); + + readonly attribute (Node or ChildNodePart)? previousSibling; + readonly attribute (Node or ChildNodePart)? nextSibling; + readonly attribute FrozenArray<Node> children; }; ``` @@ -433,8 +329,8 @@ Then we can insert two consecutive `ChildNodePart`s by relating them in the constructor as follows: ```js -const firstName = new ChildNodePart(element); -const lastName = new ChildNodePart(element, firstName); +const firstName = new ChildNodePart(document.getPartRoot(), element); +const lastName = new ChildNodePart(document.getPartRoot(), element, firstName); ``` Note that `lastName` takes `firstName` as the previous sibling but `firstName` @@ -445,8 +341,8 @@ of previous/next sibling of other parts in the constructor. An alternative is to add an explicit API to chain multiple parts togethers: ```js -const firstName = new ChildNodePart(element); -const lastName = new ChildNodePart(element); +const firstName = new ChildNodePart(document.getPartRoot(), element); +const lastName = new ChildNodePart(document.getpartRoot(), element); ChildNodePart.chain(firstName, lastName); ``` @@ -460,8 +356,8 @@ Instead of worrying about coordination, the imperative API could throw an `Error` if users constructed two `ChildNodePart`s that overlapped. ```js -const firstName = new ChildNodePart(element); -const lastName = new ChildNodePart(element); // throws an Error. +const firstName = new ChildNodePart(document.getPartRoot(), element); +const lastName = new ChildNodePart(document.getPartRoot(), element); // throws an Error. ``` Like in option 4, new APIs like `chain` or an a `append` could be added that @@ -507,8 +403,8 @@ always are scoped to a single `Node`'s children. These invisible markers would not be `Node`s and so would be backwards compatible. DOM mutations would follow shrinking `Range` semantics, meaning the -`ChildNodePart` would remove any `Node` that is removed in between the markers, -and would only add a `Node` if it is added in between the markers. +`ChildNodePart` would remove any `Node` that is removed in between the +markers,and would only add a `Node` if it is added in between the markers. ## `PropertyPart` and `CustomCallbackPart` diff --git a/proposals/DOM-Parts.md b/proposals/DOM-Parts.md index 94e9aeb4..c9874826 100644 --- a/proposals/DOM-Parts.md +++ b/proposals/DOM-Parts.md @@ -103,4 +103,4 @@ There are a few component pieces to this proposal, so it is easiest to split it into multiple documents. 1. [Imperative API to construct DOM parts](./DOM-Parts-Imperative.md) -1. [Declarative API to construct DOM parts in `<template>`](./DOM-Parts-Declarative-Template.md) +1. [Declarative API to construct DOM parts](./DOM-Parts-Declarative.md)