DEV Community

Nozomu Ikuta
Nozomu Ikuta

Posted on

Exploring How lit-html Works: TemplateResult and SVGTemplateResult constructors (4)

In this series, How lit-html works, I will explore (not explain) internal implementation of lit-html.

In the previous post, 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.

Recap of getTemplateElement() method of SVGTemplateResult class

getTemplateElement(): HTMLTemplateElement {
  const template = super.getTemplateElement();
  const content = template.content;
  const svgElement = content.firstChild!;
  content.removeChild(svgElement);
  reparentNodes(content, svgElement.firstChild);
  return template;
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
Enter fullscreen mode Exit fullscreen mode

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

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

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

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.

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;
      }
    };
Enter fullscreen mode Exit fullscreen mode

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, 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.

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>
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)