Creating Elements and Lists
A page from data
In real applications a list of products, messages or tasks isn't written in HTML in advance — it arrives as an array of data, and JavaScript builds elements from it by itself. You added an item to the cart — the array has one more object, and a new row appeared on the page.
createElement
A new element is created "in the air" and isn't visible yet:
const li = document.createElement("li"); // <li></li>
li.textContent = "Buy bread";
li.classList.add("task");
li.dataset.id = "7";
To make it appear, you need to insert it into an element that is already on the page.
Where to insert
const list = document.querySelector("#todo");
list.append(li); // at the end of list
list.prepend(li); // at the beginning of list
list.before(li); // before list (as a sibling)
list.after(li); // after list
list.append(a, b, "text"); // several at once, strings become text
One element can't be in two places: if you insert an already inserted li again, it
moves to the new place.
Removing and clearing
li.remove(); // remove the element from the page
list.replaceChildren(); // remove all children
list.innerHTML = ""; // the same thing, also very common
Rendering a list from an array
The most common technique: a render function that clears the container and builds it again from the array.
The data changed — call render() again.
const tasks = ["Wash the dishes", "Do homework", "Go for a walk"];
function render() {
const list = document.querySelector("#todo");
list.replaceChildren();
for (const text of tasks) {
const li = document.createElement("li");
li.textContent = text;
list.append(li);
}
}
render();
tasks.push("Read a book");
render();
Rebuilding everything from scratch is simpler than finding and changing individual items, and for small lists it's fast. This very idea — "the interface is a function of the data" — is later perfected by React and Vue.
Why not a string through innerHTML
You can build the markup as a string:
list.innerHTML = tasks.map((t) => `<li>${t}</li>`).join("");
It's short, but dangerous: if the text came from a user and contains tags, they will be executed (XSS).
createElement + textContent are always safe — tags in the text stay text. Strings with
innerHTML are acceptable only for your own markup that you know in advance.
A button inside a created element
Handlers can be attached to elements right after they are created — a closure will remember which item the button belongs to:
function addTask(text) {
const li = document.createElement("li");
li.append(text + " ");
const done = document.createElement("button");
done.textContent = "✓";
done.addEventListener("click", () => li.classList.toggle("done"));
li.append(done);
document.querySelector("#todo").append(li);
}
Each li has its own button, and each button remembers its own li.
Summary
document.createElement("tag")— a new element; it isn't visible until inserted.append/prepend/before/after— insert;remove()— remove;replaceChildren()— clear.- A list from an array: clear the container and create elements in a loop — a
render()function. - User text — only through
textContent, not throughinnerHTML.