Exploring How lit-html Works: TemplateResult and SVGTemplateResult constructors (4)
Tags: javascript, typescript, webcomponents, devjournal
Published: 2020-01-08 10:23:04

In this series, How lit-html works, I will explore (not explain) internal implementation of [lit-html](https://lit-html.polymer-project.org/).

In [the previous post](https://dev.to/nozomuikuta/exploring-how-lit-html-works-templateresult-and-svgtemplateresult-constructors-3-2c9n), we saw how `getHTML()` method of `TemplateResult` class handles attribute expressions and it cannot always parse comment-like string as expected.

In this post, I will explore the `reparentNodes` function, which is also one that I skipped in [the past post](https://dev.to/nozomuikuta/exploring-how-lit-html-works-templateresult-and-svgtemplateresult-constructors-g5h).

# Recap of `getTemplateElement()` method of `SVGTemplateResult` class

```typescript
getTemplateElement(): HTMLTemplateElement {
  const template = super.getTemplateElement();
  const content = template.content;
  const svgElement = content.firstChild!;
  content.removeChild(svgElement);
  reparentNodes(content, svgElement.firstChild);
  return template;
}
```

`getTemplateElement()` method removes the redundant `svg` element from the content of HTML template element, which is originally added by `getHTML()` method.

If I passed this string to `svg` tag function, the string returned by `getHTML()` method is like that below.

```typescript
const templateResult = svg`
  <svg id="my-svg">
    <circle cx="100" cy="100" r="100"/>
  </svg>
  <svg id="my-another-svg">
    <circle cx="100" cy="100" r="100"/>
  </svg>
`

console.log(templateResult.getHTML())
// => 
// <svg>
//  <svg id="my-svg">
//    <circle cx="100" cy="100" r="100"/>
//  </svg>
//  <svg id="my-another-svg">
//    <circle cx="100" cy="100" r="100"/>
//  </svg>
// </svg>
```

Then, the root `svg` element added by `getHTML()` method is removed from the content of the HTML template element.

```typescript
const svgElement = content.firstChild;
content.removeChild(svgElement);

console.log(template)
// => shown like below and #document-fragment is empty...
// <template>
//   #document-fragment
// </template>
```

After this process is the place where `reparentNodes` function comes into play.

# `reparentNodes` function

`reparentNodes` is called with the `content` as `container` and first child of the extra `svg` element, in other words, `svg` element whose id is `my-svg`.

```typescript
export const reparentNodes =
    (container: Node,
     start: Node|null,
     end: Node|null = null,
     before: Node|null = null): void => {
      while (start !== end) {
        const n = start!.nextSibling;
        container.insertBefore(start!, before);
        start = n;
      }
    };
```

In first while loop, since `start` (`svg` element whose id is `my-svg`) is not the same as `end` (`null`), the element is inserted to the `container` as the last child.

In second while loop, since `start` (`svg` element whose id is `my-another-svg`) is also not the same as `end` (`null`), the element is inserted to the `container` as the last child.

In third while loop, since `start` (`nextSibling` of the `svg` element whose id is `my-another-svg`) is the same as `end` (`null`), the loop breaks.

The reasons all the elements are inserted as the last child is that, [as written in MDN](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore), `Node.insertBefore(newNode, referenceNode)` method inserts `newNode` as the last child to the Node, if `referenceNode` node is `null`.

So, it is guaranteed that the order of elements of which the original string is consist of doesn't change.

Finally, the HTML template element is again consisted of the original elements without the redundant `svg` element.

```typescript
console.log(template)
// => shown like below...
// <template>
//   #document-fragment
//    <svg id="my-svg">
//      <circle cx="100" cy="100" r="100"/>
//    </svg>
//    <svg id="my-another-svg">
//      <circle cx="100" cy="100" r="100"/>
//    </svg>
// </template>
```

---

`reparentNodes` function is used in other places in lit-html so I will encounter it again later.

In addition, lit-html is exporting `reparentNodes` function so that you can use this function as utility if you want (e.g. extending lit-html).

# Summary

So far, I learned the following points:

- (Again) `getHTML()` method of `SVGTemplateResult` class adds a extra `svg` element to the original template so that it can be parsed in the correct namespace.
- `getTemplateElement()` method returns template element, with only the extra `svg` element removed.
- Part of this unwrapping operation is done by `reparentNodes` function, which is exported, and is available to developers.

Now, I've explored all the methods of `TemplateResult` and `SVGTemplateResult ` class.

From the next post, I will dive into the `render` function, which is another essential part of lit-html, to know how the library renders the content of HTML template element to the real DOM tree.