Non-Semantic use cases for HTML comments

Published

December 17, 2023

html

Every now and then someone on the Lit discord asks, “Hey, what’s up with all the <!--?lit$12345$--> comments all over my website’s DOM?” After some back and forth this question tends to be motivated by one or more of the following:

  1. They want to conduct unit/integration tests on their website, but the comments have random IDs that change and break tests.
  2. They are using Lit heavily and are worried about the memory cost of having tons of comment nodes.
  3. They may just not want to see comment nodes in the developer tools when debugging because it adds noise.

To address the elephant in the room upfront, HTML comments can be used for more than documentation.

The point of this post is to give some functional reasons for why comment nodes are used by libraries! After reading this, you may reach for HTML comments for more than just documentation.

Addressing the user’s root issues with comment nodes

Before diving into some concrete use cases of comment nodes, let’s address the three points listed above from our straw person user.

I want to conduct unit/integration tests, but comment nodes are making testing harder

When testing DOM, it’s preferred to test semantics instead of plain innerHTML text which is often much noisier and can contain details not specific to the test.

Instead of selecting an element and asserting the element’s innerHTML, prefer to assert the specific properties that you care about. And, if you really care about the HTML contents, then strip out the comments, or check for only the substrings that matter for the test.

Comments nodes are memory intensive

A common tool when doing preliminary research on performance is generating a Lighthouse performance score, where you may receive the following guidance:

Lighthouse warning with text: Avoid an excessive DOM size - 2,081 elements

Comment nodes may seem like an easy win here, but they aren’t counted by the audit, so removing them won’t fix the Lighthouse suggestion. More holistically, although comment nodes do have some memory cost, their performance cost when considered for style calculation, layout and paint is negligible.

Comment nodes can add developer tool noise

This is a stylistic objection and is a bit tougher to respond to. I agree that you want to minimize comments. Documentation comment nodes should already follow good documentation practices.

When comment nodes are used functionally, it can be useful to see them. If you really want to hide HTML comments, they can be hidden in Chrome’s developer tool settings.

With the objections to comment nodes addressed, let’s see how we can use them for more than documentation!

Use case 1: Break up text nodes

It’s not possible to represent two adjacent Text nodes declaratively. But with comment nodes, you can separate text content into separate text nodes split by the comment node.

<span>
  Text node 1
  <?>
  Text node 2
</span>
Text node 1 Text node 2
Live example of two text nodes split by a comment.

If you select the <span> element and inspect Element.prototype.childNodes, then you’ll find three nodes: [text, comment, text].

This technique is useful when you need to split text nodes from declarative HTML source code. Maybe you want to keep a reference to a dynamic text node that will be updated with Text.prototype.textContent without touching the static sibling content.

Use case 2: Inexpensive marker in the DOM

Sometimes you need to mark a location in the DOM, and you don’t want to use an Element or something that would contribute to style calculations, layout, or paint. Use a comment node!

A small and trivial example is inspired by how Lit marks a dynamic child part in the DOM.

Imagine you have a Template element containing some DOM contents that you want to clone onto the page. It has some static DOM, but also contains some dynamic text that shows the time, and changes over time.

How can we represent the dynamic location in the static Template contents? We can leave a comment! Then later we can obtain a reference to the comment and imperatively hook up the dynamic DOM.

Live example of cloning DOM from a Template element and using a comment to mark the dynamic time position.

The slightly trivial example of code that drives the example above follows. Take a read over it and note how the comment is used as a cheap marker for inserting dynamic content.

const container = document.querySelector('#container');
// Simulate instantiating some DOM that was stored in a
// template element.
const template = document.createElement('template');
template.innerHTML = `<span>The time is: <?></span>`;

// Instantiate that static template content, inserting a
// text node after the comment.
const clonedFragment = template.content.cloneNode(true);
const walker = document.createTreeWalker(
  clonedFragment,
  NodeFilter.SHOW_COMMENT
);
// In a more sophisticated example, you would walk
// multiple comment nodes, and map them to a list
// of dynamic parts.
const comment = walker.nextNode();
const textNode = new Text();
comment.after(textNode);
setInterval(() => {
  textNode.textContent = new Date().toLocaleTimeString('en-US');
}, 1000);

// Finally, append the instantiated DOM with dynamic
// bindings into the live tree.
container.append(...clonedFragment.children);

Conclusion

Comments can do more than only documenting source code! If you’ve read this far, you will probably enjoy reading the DOM Parts proposal. This proposal would provide a browser native way of achieving what I’ve described in “Use case 2” above. Especially via the DOM Parts Declarative Template API proposal.

If you know any other use cases for comment nodes, please share it with me! I’d love to hear about it!