Topic 8.6
Creating, Inserting and Removing Elements
In one line
document.createElement makes a new element that lives nowhere until you insert it with append, prepend, before, after or replaceWith; remove takes it out, inserting an existing element moves it, and <template> gives you ready-made markup to clone.
Think of it like this
Building with LEGO. First you take a new brick out of the box (createElement): it exists in your hand, but it isn't part of the model yet. You decide its colour and stickers (set its text, classes and attributes). Then you click it onto the model at a chosen spot: on top, at the bottom, next to another brick (append, prepend, before, after). You can pull a brick off (remove), swap one brick for another (replaceWith), or move a brick: pulling it from one place and clicking it somewhere else. One brick can't be in two places at once.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- createElement
- Makes a new, empty element of the tag you name, not yet on the page.
- Detached
- An element that exists in memory but isn't inserted in the document, so it isn't shown.
- append / prepend
- Insert nodes or text at the end / at the start inside an element.
- before / after
- Insert nodes or text next to an element, outside it, as siblings.
- replaceWith
- Take an element out and put other nodes in its place.
- remove
- Take an element out of the document.
- cloneNode
- Make a copy of a node;
cloneNode(true)copies everything inside it too. - template
- An HTML element whose contents are stored but not shown, used as a stamp to make copies.
- DocumentFragment
- A light container for nodes that isn't part of the page; inserting it inserts its children.
Step by step
01Create and set up
Make the element and give it everything it needs before it goes on the page.
const li = document.createElement("li");
li.textContent = "Buy milk";
li.classList.add("task");
li.dataset.id = "1";
console.log(li.isConnected); // false: not in the document yet02Insert at the right spot
Choose the method by where the new node goes relative to an element you already have.
03Insert many at once
append takes several items. Mixing elements and strings is fine; strings become text.
const p = document.createElement("p");
const strong = document.createElement("strong");
strong.textContent = "Asha";
p.append(strong, " joined the class.");
document.body.append(p);04Move, copy, replace, remove
Appending an existing element moves it. Clone it if you want two. replaceWith swaps; remove deletes.
done.append(task); // moves task from the todo list to the done list
const copy = card.cloneNode(true); // a second card
copy.id = "card-2";
oldBanner.replaceWith(newBanner);
spinner.remove();05Stamp out copies from a template
Keep the markup in HTML, clone it for each item, fill it with textContent, and append. No HTML strings in your JavaScript, so no XSS risk.
<template id="row-tpl">
<tr><td class="name"></td><td class="score"></td></tr>
</template>
<table><tbody id="rows"></tbody></table>Try it yourself
- 1
Copy instead of move
In the first example, change the last
list.append(first)tolist.append(first.cloneNode(true)). Predict the final list, then run. - 2
Try all four positions
In the second example, call
box.insertAdjacentHTMLwith"beforebegin","afterbegin"and"afterend"too. Predict where each paragraph lands, then check in the preview. - 3
Add a product
In the third example, add
{ name: "Desk", price: 2499 }to the array and give each card adata-namewith the product name. Log thedataset.nameof every card.
Code & diagrams
`append` returns `undefined`. The string passed to `after` became a text node. Appending `first` again moved it to the end; it didn't make a copy.
Preview
Console
Nothing logged yet.Expected console output
false
true
undefined
[ 'First', 'Second', 'Third' ]
<h3> #text 'Total: 3 items'
[ 'Second', 'Third', 'First' ]A removed element still exists while a variable holds it, so `replaceChildren(gone)` could put it back, after emptying the box in the same call.
Preview
Console
Nothing logged yet.Expected console output
false Delete me
[ 'new', 'copy', 'adj' ]
null
1 <p#gone>The template's card isn't in the document, so `querySelectorAll(".card")` found 0 at first. Each clone was filled and appended; the template itself stayed empty for the next use. `<Deluxe>` is shown as text because it went in through `textContent`.
Preview
Console
Nothing logged yet.Expected console output
#document-fragment 0
2
Chair <Deluxe>
trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Create an element but never insert it
Write const li = document.createElement("li"); li.textContent = "New"; and nothing else.
Break #2
Append the same element to several lists
Make one badge element and call cardA.append(badge); cardB.append(badge);.
Break #3
Append the template itself
Write list.append(document.getElementById("row-tpl")); instead of cloning its content.
Myth vs fact
Myth
append and appendChild are the same.
Fact
append accepts several nodes and strings and returns undefined; appendChild accepts exactly one node (no strings) and returns it. Both add at the end.
Myth
cloneNode(true) makes a perfect copy, including behaviour.
Fact
It copies the element, attributes and descendants, but not listeners added with addEventListener, not the current value of properties that aren't attributes (for most inputs), and it duplicates ids.
Myth
A removed element is deleted immediately.
Fact
remove() detaches it. It lives on while any variable or listener refers to it, and can be inserted again. Holding references to many removed elements is a common memory leak.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Strings given to
append,prepend,before,after,replaceWithandreplaceChildrenare inserted as text nodes, so they are XSS-safe sinks. Only the...HTMLmethods and properties parse markup. - ▸
When several nodes are passed to
append(or a fragment is inserted), the DOM inserts them in one operation: one mutation record, and one style invalidation. The rendering engine still won't lay out until the next frame or until you read layout (Topic 8.8). - ▸
Moving a node with
appendis really remove-then-insert: an iframe inside it reloads, a playing video can restart, CSS animations restart and focus is lost. Chromium addedparent.moveBefore(node, ref)to move nodes while keeping that state; check support before using it. - ▸
document.importNode(tpl.content, true)is similar tocloneNode(true)but adopts the nodes into the current document, which matters for templates from another document (for example, fromDOMParser). For a template in the same page,cloneNode(true)is enough.
Remember this
- 1
document.createElement("li")creates a new element object. At first it is detached: it is not in the document, so it isn't visible anddocument.querySelectorcan't find it. Set it up while it's detached:li.textContent = "Milk",li.className = "item",li.dataset.id = "7".document.createTextNode("text")makes a text node, though you rarely need it because the insert methods accept strings. - 2
The modern insert methods are called on an element and accept any number of nodes or strings (strings become text nodes, never HTML, so they are safe for user data).
parent.append(a, b)adds at the end inside the parent;parent.prepend(a)adds at the start inside.el.before(a)andel.after(a)add outside, as siblings before or afterel.el.replaceWith(a)swapselout.parent.replaceChildren(a, b)removes all children and inserts the new ones (with no arguments, it empties the parent). - 3
The older methods are still everywhere in existing code:
parent.appendChild(node)(one node only, no strings, and it returns the node),parent.insertBefore(newNode, referenceNode),parent.removeChild(child)andparent.replaceChild(newChild, oldChild). They do the same jobs with a clumsier shape.appendreturnsundefined, which surprises people who chain fromappendChild. - 4
A node can only be in one place. If you insert an element that's already in the document, it is moved, not copied. To copy, call
el.cloneNode(true):truemeans a deep copy with all descendants;falsecopies only the element itself. Clones copy attributes but not event listeners added withaddEventListener, and the copy keeps the sameid, which you must change to keep ids unique. - 5
el.remove()takes an element out of the document. The object still exists while a variable points to it, so you can insert it again later; once nothing refers to it, the garbage collector frees it.insertAdjacentHTML(position, html)andinsertAdjacentElement(position, el)insert at one of four positions:"beforebegin"(before the element),"afterbegin"(inside, first),"beforeend"(inside, last),"afterend"(after the element).insertAdjacentHTMLparses HTML, so it follows the same XSS rules asinnerHTML(Topic 8.3). - 6
The
<template>element holds HTML that the browser parses but doesn't show or run: images don't load, scripts don't run, and its contents aren't in the document tree.template.contentis a DocumentFragment (a lightweight container that is not part of the page; Topic 8.8 uses it for speed). To use it, clone the content (template.content.cloneNode(true)), fill in the clone withquerySelectorandtextContent, and append it. This keeps your markup in HTML, where it's easy to read, and fills it with data safely.
Explain it without notes
Walk through creating a list item with text and a class and adding it as the first item of a list.
What happens when you append an element that is already in the document? How do you get a copy instead?
What is the <template> element for, and how do you use it?
Compare append with appendChild, and remove with removeChild.
Practice
Write addTask(text) that appends an <li class="task"> with the given text to #tasks and returns the new element. Add two tasks and log the list's texts.
Write moveToDone(li) that moves a task from #todo to the start of #done, and log both lists' lengths afterwards.
Using a <template> with a <tr>, render the rows [["Asha", 91], ["Ravi", 78]] into a <tbody> and log the number of rows.
Trade-offs
- ↔
createElementcode is verbose but safe and gives you references to every element;<template>keeps markup readable in HTML;insertAdjacentHTMLwith your own fixed strings is shortest but must never receive untrusted data. - ↔
Rebuilding a whole list with
replaceChildrenis simple and always correct; updating only the changed items is faster for long lists and keeps focus and scroll, but needs more code (this is what frameworks automate).
Done when you can
I can create an element, set its text, classes and data, and insert it.
I can choose between
append,prepend,before,after,replaceWithandreplaceChildren.I know inserting an existing element moves it, and I can copy with
cloneNode(true).I can remove elements and empty containers.
I can render repeated markup from a
<template>.I can read older code that uses
appendChild,insertBeforeandremoveChild.