How do you build a DOM subtree in JavaScript?
You create the elements, configure them, attach the children to their parents, and only then attach the whole thing to the document. The order is what makes it fast, because until a node is on the page the browser does no layout work for it.
// A tiny stand-in for the browser DOM, so this example runs in Node.
const element = (tag, attrs = {}) => ({
tagName: tag.toUpperCase(),
attributes: attrs,
children: [],
setText(value) { this.text = value; return this; },
appendChild(child) { this.children.push(child); return child; },
});
const list = element("ul", { class: "topics" });
for (const topic of ["Closures", "Promises", "Prototypes"]) {
const item = element("li");
item.setText(topic);
list.appendChild(item);
}
console.log(list.tagName); // "UL"
console.log(list.attributes.class); // "topics"
console.log(list.children.length); // 3
console.log(list.children.map((c) => c.text)); // ["Closures", "Promises", "Prototypes"]Two variations of the loop are worth knowing. You can build the string of markup first and assign it with innerHTML in one operation, which is faster for large chunks of static content but loses any listeners and is only safe when the markup is trusted. Or you can build a document fragment, which is a container that lives outside the document, and attach the finished result in one call.
Another detail worth raising: the appendChild method moves a node rather than copying it. If the node is already somewhere in the document, appending it somewhere else takes it away from its old position. That is exactly what you want when you are rearranging a list, and it is a bug when you assumed you were cloning.
And a note on text. Use the textContent property to set the text of a node, and reserve innerHTML for markup you built yourself. A user-supplied string pasted into innerHTML is the classic cross-site scripting bug.