A list that grows, a notification that pops up, a table row added after a save: none of those exist in the original HTML file. They are built while the page is alive.
One method is enough to create them, but it only covers half the journey: the element it produces stays invisible until you attach it to the page.
Definition
document.createElement() builds an HTML element from its tag name and returns it empty, with no parent and absent from the page. It is an object in memory, ready to be filled and then inserted.
const button = document.createElement("button");
button.textContent = "Submit";
button.className = "primary";
button.type = "button";
console.log(button.parentNode); // null, it lives nowhere
document.body.append(button); // now it is in the pageThe tag name is not case sensitive in an HTML document: createElement("DIV") and createElement("div") produce the very same element.
Create, fill, insert
Building an element always happens in three steps, and forgetting the third one is the most common mistake.
- Create with
createElement, which returns an element detached from the document. - Fill its properties: textContent for text, classList for classes, dataset for custom data,
setAttributefor everything else. - Insert with
append,prepend,beforeorafter, depending on the position you want.
append accepts several arguments and plain strings, where the older appendChild takes a single node and returns it.
Inserting several elements at once
Adding a hundred elements one by one triggers a hundred changes to the document. A fragment acts as an intermediate bag: you fill it outside the page, then empty it into the page in a single operation.
const data = ["Paris", "Lyon", "Nantes"];
const fragment = document.createDocumentFragment();
for (const city of data) {
const item = document.createElement("li");
item.textContent = city;
fragment.append(item);
}
document.querySelector("ul").append(fragment);
// the fragment is emptied of its children, only the li elements remainA for loop paired with of is enough here, but the principle is the same with Array.forEach() or Array.map().
An element that is created and never inserted is not a leak: it disappears as soon as no variable references it. An element removed from the page but kept in an array, on the other hand, stays in memory along with its listeners.
Frequently asked questions
createElement or innerHTML to build a list?
Both work, but they do not carry the same risk. innerHTML parses the string as markup, so any data coming from outside has to be escaped first. Building element by element cannot produce an unintended tag, and it lets you attach listeners to each node at the moment it is created.
Why does my element not show up?
In almost every case the insertion is missing: the element was created and filled, but never attached to a parent that lives in the page. The second cause is a parent that cannot be found, when the script runs before the HTML has been parsed. Check the value returned by querySelector() before looking any further.
How do I create an SVG icon?
Not with this method: an SVG belongs to a different namespace, and an element created by createElement will not render. You need document.createElementNS, passing the SVG namespace address as the first argument. That is the only difference, the rest of the build is identical.