Most interactive pages build part of their content with JavaScript. A to-do app adds a list item when you press Enter. A shop renders product cards from data sent by a server. A chat window appends each new message. Comment sections, notifications, search results and table rows all appear on the page without a reload, because JavaScript creates elements and inserts them into the DOM.
In the DOM manipulation lesson you changed the text, classes and styles of elements that were already in the HTML. This lesson goes further: creating brand new elements, putting them exactly where you want, removing or replacing them, and building whole lists from arrays of data.
You'll also learn DOM traversal: moving from one element to its parent, children or neighbours. That's essential when a click on a "Delete" button needs to find and remove the card it belongs to. And you'll learn how to insert content safely, so text typed by users can never inject harmful HTML into your page.
Creating and inserting elements
const li = document.createElement("li");
li.textContent = "Buy milk";
li.className = "task";
document.querySelector("#list").append(li);| Method | Puts the new content… |
|---|---|
parent.append(a, b) | At the end, inside the parent (accepts elements and text) |
parent.prepend(a) | At the start, inside the parent |
el.before(a) | Just before el, as a sibling |
el.after(a) | Just after el, as a sibling |
el.replaceWith(a) | In place of el |
parent.appendChild(a) | At the end (older method, one element only) |
An element can only be in one place at a time: appending an element that's already on the page moves it.
Removing and copying
el.remove()takes an element out of the page.parent.replaceChildren()with no arguments empties the parent; with arguments it swaps in new children.el.cloneNode(true)copies an element and everything inside it (but not its event listeners).el.hidden = truehides without removing, if you'll show it again soon.
Removing an element also removes its children. Any event listeners on removed elements are cleaned up once nothing else refers to them.
Building from data safely
| Approach | Good for | Watch out |
|---|---|---|
createElement + textContent | Anything with user data | More lines of code |
insertAdjacentHTML("beforeend", html) | Quick markup from trusted strings | Never put user input in the string |
innerHTML = html | Replacing a whole area at once | Destroys existing elements and listeners; same safety rule |
<template> + content.cloneNode(true) | Repeated components | Fill in values with textContent |
textContent always treats data as plain text, so a comment containing <script> or <img onerror=...> is shown harmlessly instead of running. This protects against cross-site scripting (XSS), one of the most common web security problems.
Traversing the DOM
| Property or method | Finds |
|---|---|
el.parentElement | The parent element |
el.children | Child elements (a live collection) |
el.firstElementChild, el.lastElementChild | First and last child elements |
el.nextElementSibling, el.previousElementSibling | Neighbours |
el.closest(".card") | The nearest ancestor (or itself) matching a selector |
el.querySelector(".price") | The first matching descendant |
el.contains(other) | Whether other is inside el |
closest is the star: from a clicked button, event.target.closest("li") finds the list item it belongs to, however deeply the button is nested.
JavaScript Create Elements Syntax
Creating and traversing
const card = document.createElement("article");
card.className = "card";
card.textContent = product.name;
grid.append(card);
card.remove();
oldEl.replaceWith(newEl);
list.replaceChildren(...items);
btn.closest(".card");
card.parentElement; card.children; card.nextElementSibling;JavaScript Create Elements Summary
document.createElementmakes an element;append,prepend,beforeandafterinsert it.remove,replaceWithandreplaceChildrentake elements out or swap them.- Use
textContentfor user data to prevent XSS; avoid putting user input ininnerHTML. <template>elements hold reusable markup that you clone and fill.closest,parentElement,childrenand the sibling properties move around the DOM tree.
JavaScript Create Elements Examples
Each example runs in the page. Edit the code and press Run to see what changes.
A to-do list built with createElement
Type a task and press Add. Click a task to remove it.
<form id="add">
<input id="task" placeholder="New task" required>
<button>Add</button>
</form>
<ul id="list"></ul>
<p id="empty">Nothing to do.</p>const form = document.querySelector("#add");
const input = document.querySelector("#task");
const list = document.querySelector("#list");
const empty = document.querySelector("#empty");
form.addEventListener("submit", (e) => {
e.preventDefault();
const li = document.createElement("li");
li.textContent = input.value;
li.title = "Click to remove";
li.addEventListener("click", () => {
li.remove();
empty.hidden = list.children.length > 0;
});
list.append(li);
empty.hidden = true;
input.value = "";
input.focus();
});Line-by-line explanation
- Line 7Stop the form reloading the page.
- Lines 8 to 9Create a list item and set its text safely.
- Lines 11 to 14Clicking the item removes it, and the "empty" message returns when the list is empty.
- Line 15Insert the new item at the end of the list.
Rendering cards from data with a template
<template id="card-tpl">
<article class="card">
<h3 class="name"></h3>
<p class="price"></p>
<button class="remove">Remove</button>
</article>
</template>
<div id="grid"></div>
<style>
#grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; font-family: sans-serif; }
.card { border: 1px solid #ddd; border-radius: 10px; padding: 10px; }
</style>const products = [
{ name: "Desk lamp", price: 24 },
{ name: "Notebook <b>A5</b>", price: 6.5 },
{ name: "Mug", price: 9 },
];
const tpl = document.querySelector("#card-tpl");
const grid = document.querySelector("#grid");
const cards = products.map((p) => {
const card = tpl.content.cloneNode(true);
card.querySelector(".name").textContent = p.name;
card.querySelector(".price").textContent = `£${p.price.toFixed(2)}`;
return card;
});
grid.append(...cards);
grid.addEventListener("click", (e) => {
if (e.target.matches(".remove")) e.target.closest(".card").remove();
});Line-by-line explanation
- Line 3Data containing HTML tags is shown as plain text, not run as HTML.
- Lines 10 to 15Clone the template for each product and fill in its text.
- Line 16Insert all the cards in one go.
- Lines 18 to 20One listener on the grid finds the clicked card with
closestand removes it.
Moving around the DOM tree
<ul id="steps">
<li>Mix</li>
<li id="bake">Bake <button>Done</button></li>
<li>Cool</li>
</ul>const bake = document.querySelector("#bake");
const btn = bake.querySelector("button");
console.log("Parent:", bake.parentElement.id);
console.log("Previous:", bake.previousElementSibling.textContent);
console.log("Next:", bake.nextElementSibling.textContent);
console.log("Children of list:", bake.parentElement.children.length);
console.log("closest li:", btn.closest("li").id);
const note = document.createElement("li");
note.textContent = "Preheat oven";
bake.before(note);
bake.parentElement.lastElementChild.after(Object.assign(document.createElement("li"), { textContent: "Serve" }));
console.log([...document.querySelector("#steps").children].map(li => li.firstChild.textContent.trim()));Line-by-line explanation
- Lines 4 to 7Move up to the parent and sideways to the neighbours.
- Line 8From the button,
closestclimbs up to its list item. - Line 12Insert a new step just before "Bake".
- Line 13Add a final step after the last one.
Common JavaScript Create Elements Mistakes
Putting user input into innerHTML
comments.innerHTML += `<li>${userComment}</li>`; // can run injected HTMLconst li = document.createElement("li");
li.textContent = userComment;
comments.append(li);Adding elements one by one in a loop
for (const item of items) list.innerHTML += `<li>${item}</li>`; // rebuilds the list every timelist.append(...items.map(item => Object.assign(document.createElement("li"), { textContent: item })));Using parentNode chains
btn.parentNode.parentNode.parentNode.remove(); // breaks when the HTML changesbtn.closest(".card").remove();JavaScript Create Elements in Real Projects
Creating elements in real apps
Infinite-scroll feeds append new posts as you reach the bottom. Shopping baskets add and remove rows as you change your order. Notification systems create a toast element, insert it, then remove it after a few seconds. Frameworks like React and Vue do all of this for you behind the scenes, but they're built on exactly these DOM methods.
JavaScript Create Elements Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Create a paragraph element and add it to the body.
const p = document.("p");
p.textContent = "Hello";
document.body.(p);
createElement, insert with append.What is logged?
const ul = document.createElement("ul");
const a = document.createElement("li");
const b = document.createElement("li");
ul.append(a, b);
ul.prepend(b);
console.log(ul.children.length, ul.firstElementChild === b);Which removes an element from the page?
Why use textContent instead of innerHTML for user data?
addRow(name) should add a new <li> with the name as text to the end of #people, and each row's button should remove only its own row. Fix it.
<ul id="people"></ul>function addRow(name) {
const li = document.createElement("li");
li.innerHTML = name + ' <button>x</button>';
document.querySelector("#people").prepend(li);
li.querySelector("button").addEventListener("click", () => {
document.querySelector("#people").remove();
});
}
addRow("Ana");
addRow("<b>Ben</b>");- Rows are added at the end
- Names are text, not HTML
- A button removes only its row
JavaScript Create Elements Quiz
5 questions to check what you learned.
Which method adds content at the start of a parent?
- prepend
- append
- before
- after
What does el.closest(".card") return?
- The next .card on the page
- The nearest ancestor (or el itself) matching .card
- All .card children
- The first .card in the document
What happens if you append an element that's already on the page?
- It's copied
- An error
- Nothing
- It moves to the new place
What is a template element for?
- Styling forms
- Loading scripts
- Defining CSS variables
- Holding inert markup you can clone and fill with JavaScript
Why insert many elements with one append(...items) call?
- append only accepts arrays
- One insertion means one layout update instead of many
- It removes duplicates
- It makes elements focusable
JavaScript Create Elements Challenge
Render a scoreboard
Write renderScores(scores) where scores is an array like [{ name: "Mia", points: 12 }]:
- Empty
#boardfirst (so calling it twice doesn't duplicate rows) - Sort a copy of the scores from highest to lowest points
- For each score, create an
<li>whose text is`${name}: ${points}`, usingtextContent - Give the first (top)
<li>the classleader - Append all the items to
#board
<ol id="board"></ol>function renderScores(scores) {
}
renderScores([{ name: "Mia", points: 12 }, { name: "Raj", points: 30 }, { name: "Lee", points: 18 }]);- Rows are sorted highest first
- Leader is marked
- Calling again doesn't duplicate
- Names are added as text