DOM and events · 4 / 10
lesson 4

Creating and inserting

createElement, append, prepend, replaceChildren, insertAdjacentHTML — the modern set, and when to reach for which.

~ 13 min read·lesson 4 of 10
0 / 10

The previous lesson was about reading and changing what is already there. This one is about building new pieces of the page. You will do this constantly — render a list of items, add a row to a table, pop in an error message, swap one panel for another.

The modern set of tools is small and well-named. The pre-modern alternatives (appendChild, insertBefore, building elements by string concatenation) still work, but the new methods are easier and harder to get wrong.

createElement

The way to make a fresh element is document.createElement.

create.js
const tag = document.createElement('span');
tag.className = 'tag';
tag.textContent = 'beta';

// nothing on the page yet — the element exists in memory only

Three things happen here. First, a <span> is created in memory, detached from the page. Second, you set its class. Third, you set its text. None of this is visible to the user yet. The element is real but homeless — it has no parent.

You connect it to the page in the next step, by appending it somewhere.

connect.js
document.body.append(tag);
// the span is now a child of body, visible on the page

This split — create offscreen, then attach — is the right shape for any non-trivial UI you build by hand. While the element is detached, you can configure it freely. The browser does not need to render anything until the moment you connect it.

Tip

Setting many properties on a detached element is much cheaper than setting them on a connected one. The browser does not have to schedule layout work for changes nobody can see. Build, then attach.

append and prepend

Once you have an element to attach, the two methods you reach for are append and prepend.

  • parent.append(...) adds children to the end of parent.
  • parent.prepend(...) adds children to the start of parent.

Both take any number of arguments. You can pass elements, strings, or a mix.

append.js
const list = document.querySelector('ul');
const item = document.createElement('li');
item.textContent = 'New item';

list.append(item);          // adds at the bottom of the list
list.prepend('Pinned: ', item); // strings become text nodes; mixed args are fine

Strings become text nodes automatically. So list.append('Hello') adds a text node containing "Hello" — not the literal characters of an <li>. That is exactly what you want for adding readable text alongside elements.

You will see older code use parent.appendChild(item). It does the same thing for one element argument and only one — appendChild does not accept strings or multiple arguments. append is the strict superset; prefer it.

Watch out

Appending an element that is already in the DOM moves it. The browser does not clone — the element is detached from its old parent and reattached to the new one. If you want a copy, call node.cloneNode(true) first.

check your understanding
You have an item already inside list A. You call listB.append(item). What is true afterwards?

replaceChildren — the nuke

A common job: empty a container, then put new content in it. Pre-modern code did this with a loop or by setting innerHTML = '' first. The modern method is replaceChildren.

replace.js
const tagList = document.querySelector('.tags');

// Wipe everything, then put new tags in:
tagList.replaceChildren(...newTagElements);

// Empty everything, no replacements:
tagList.replaceChildren();

replaceChildren() with no arguments clears the element. With arguments, it removes all current children and appends the arguments in order. The whole operation is one call — the browser only re-layouts once.

This is the right tool for "render this list from scratch" — render the array, build elements for each item, and replaceChildren(...newItems). Clean and atomic.

check your understanding
You re-render a notifications panel every few seconds with the latest 5 items. You build the new li elements as an array items. Which call is the cleanest way to put them into the existing <ul>?

insertAdjacentHTML

Sometimes you have a chunk of HTML as a string and want to drop it next to (not inside) an existing element. insertAdjacentHTML handles that.

It takes two arguments: a position and an HTML string. The position is one of four words: 'beforebegin', 'afterbegin', 'beforeend', 'afterend'.

adjacent.js
const card = document.querySelector('.card');

card.insertAdjacentHTML('afterbegin',  '<h3>Title</h3>');     // inside, at the very start
card.insertAdjacentHTML('beforeend',   '<p>Footer.</p>');     // inside, at the very end
card.insertAdjacentHTML('beforebegin', '<hr>');               // outside, just above
card.insertAdjacentHTML('afterend',    '<hr>');               // outside, just below

Two warnings come with this method.

One, the same XSS rule applies as with innerHTML: the string is parsed as HTML, so any tags inside it become real elements. Never feed user input to insertAdjacentHTML without sanitizing.

Two, this is for the cases where building elements is unnecessarily verbose — like "add this static panel above the form". For dynamic content with data, build elements with createElement and append. Code is easier to read and the security story is simpler.

Watch out

insertAdjacentHTML parses HTML the same way innerHTML does. The same rule applies — never feed it untrusted strings.

Removing nodes

To remove a node from the DOM, call node.remove(). That is the whole API.

remove.js
const error = document.querySelector('.error');
error.remove();
// the element is detached from the document

The element still exists as an object in your code (if you held a reference), but it is no longer in the live tree, so the user does not see it. You can re-append it later if you want — same element, back on the page.

You will sometimes see older code call parent.removeChild(child). It does the same thing but requires a reference to the parent. child.remove() does not — the element knows where it lives. Use remove.

check your understanding
You want to insert a new section just below an existing <header> at the top of the page, but not inside it. Which call lands the section as the next sibling of the header?
check your understanding
You build 200 list items inside a forEach and want to add them all to a list. Which approach minimizes layout work?
← prevnext lesson →
KeepLearningcertificate
for completing
DOM and events
0 of 10 read