JavaScript Core/
Lesson

Real applications do not just modify what is already on the page, they build entire interfaces on the fly. Think about a social media feed where new posts appear as you scroll, or a todo list where items appear as you type. This is dynamic element creation in action.

The lifecycle of a DOMWhat is dom?The Document Object Model - the browser's live representation of your HTML page as a tree of objects that JavaScript can read and modify. element

Every created element goes through three stages:

  1. Creation: you create the element in JavaScript memory
  2. Configuration: you set its content, classes, attributes, and event listeners
  3. Insertion: you attach it to the DOM tree, making it visible

Until step 3, the element exists only in memory. The user cannot see it.

02

Creating elements

The foundation is document.createElement():

const paragraph = document.createElement('p');
paragraph.textContent = 'This is a dynamically created paragraph!';
paragraph.classList.add('intro-text');
paragraph.id = 'dynamic-para';

// Exists in memory but NOT on the page yet

Building complex structures

const card = document.createElement('div');
card.classList.add('card');

const title = document.createElement('h3');
title.textContent = 'Product Name';

const description = document.createElement('p');
description.textContent = 'An amazing product.';

const button = document.createElement('button');
button.textContent = 'Buy Now';
button.classList.add('btn', 'btn-primary');

card.appendChild(title);
card.appendChild(description);
card.appendChild(button);

This is more verbose than innerHTML, but it is safe by default. Every piece of text goes through textContent, which cannot execute scripts. Compare:

// SAFE: textContent escapes everything automatically
title.textContent = userInput;  // "<script>alert('hack')</script>" displays as plain text

// DANGEROUS: innerHTML executes scripts from user input
title.innerHTML = userInput;    // "<script>alert('hack')</script>" runs the script!
AI pitfall, innerHTML instead of createElement: AI almost always generates container.innerHTML = '<div class="card"><h3>' + title + '</h3></div>' instead of using createElement. This is faster to write but creates an XSS vulnerability if any variable contains user input. The variable could contain <img src=x onerror="steal(document.cookie)"> and innerHTML would execute it. Even when AI does use createElement, it often sets content with el.innerHTML = userInput rather than el.textContent = userInput. Always use createElement combined with textContent when building elements from dynamic data. Reserve innerHTML for static HTML templates that you wrote yourself and control entirely.
03

Inserting elements

Once your element is ready, attach it to the document:

MethodPositionNotes
parent.appendChild(el)Last childClassic, returns the appended element
parent.append(el)Last childModern, accepts multiple nodes and strings
parent.prepend(el)First childAdds at the beginning
parent.insertBefore(el, ref)Before referenceClassic, requires parent context
ref.before(el)Before siblingModern, called on the reference element
ref.after(el)After siblingModern, called on the reference element
const list = document.querySelector('#todo-list');
const newItem = document.createElement('li');
newItem.textContent = 'Buy groceries';

list.appendChild(newItem);  // adds as last child

// Or insert before a specific element
const firstItem = list.querySelector('li');
list.insertBefore(newItem, firstItem);  // adds before first item

insertAdjacentHTML

For quick HTML insertion (use only with trusted strings):

container.insertAdjacentHTML('beforeend', '<p>Last child inside</p>');
container.insertAdjacentHTML('afterbegin', '<p>First child inside</p>');
Caution
Like innerHTML, insertAdjacentHTML parses HTML strings. Never use it with user-generated content.
04

Removing elements

const item = document.querySelector('#item-123');
item.remove();  // gone from the page

// Classic way (via parent)
const parent = document.querySelector('#list');
const child = document.querySelector('#item-123');
parent.removeChild(child);  // returns the removed element

Cleaning up before removal

Before removing complex components, clean up associated resources:

function removeTodoItem(itemElement) {
  const deleteBtn = itemElement.querySelector('.delete-btn');
  deleteBtn.removeEventListener('click', handleDelete);

  clearTimeout(itemElement.dataset.timeoutId);

  itemElement.remove();
}
05

Performance: batchingWhat is batching?Grouping multiple state updates together into a single re-render cycle instead of re-rendering after each individual update. with DocumentFragment

Creating many elements individually is slow because each insertion triggers browser reflow. Use DocumentFragment to batch:

// Slow: 1000 DOM updates
const list = document.querySelector('#big-list');
for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i}`;
  list.appendChild(item);  // triggers reflow each time
}

// Fast: 1 DOM update
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i}`;
  fragment.appendChild(item);  // no reflow - fragment is in memory
}
list.appendChild(fragment);  // single reflow
06

Real-world pattern: dynamic todo list

function createTodoElement(text, id) {
  const li = document.createElement('li');
  li.classList.add('todo-item');
  li.dataset.id = id;

  const span = document.createElement('span');
  span.textContent = text;  // safe - uses textContent, not innerHTML

  const deleteBtn = document.createElement('button');
  deleteBtn.textContent = 'Delete';
  deleteBtn.classList.add('btn-delete');

  li.appendChild(span);
  li.appendChild(deleteBtn);

  return li;
}

// Usage with event delegation on the parent (no per-item listeners needed)
const todoList = document.querySelector('#todos');
todoList.appendChild(createTodoElement('Buy groceries', 123));

todoList.addEventListener('click', (e) => {
  if (e.target.classList.contains('btn-delete')) {
    e.target.closest('.todo-item').remove();
  }
});

Notice the pattern: createElement + textContent for safe content, event delegationWhat is event delegation?Attaching one event listener to a parent element instead of many listeners to each child, using event bubbling to catch events from children. on the parent for efficient click handling, and closest() to find the parent li from the button click.

07

Replacing and cloning

// Replace an element
const oldEl = document.querySelector('.old');
const newEl = document.createElement('div');
newEl.textContent = 'Replacement!';
oldEl.replaceWith(newEl);

// Clone an element (true = deep clone with children)
const template = document.querySelector('.card-template');
const clone = template.cloneNode(true);
clone.querySelector('.title').textContent = 'New Title';
document.querySelector('.container').appendChild(clone);
08

Quick reference

TaskCode
Create elementdocument.createElement('div')
Set text (safe)el.textContent = 'text'
Add classel.classList.add('name')
Append as last childparent.appendChild(el)
Prepend as first childparent.prepend(el)
Insert before siblingref.before(el)
Remove elementel.remove()
Batch insertsdocument.createDocumentFragment()
Clone elementel.cloneNode(true)
Replace elementel.replaceWith(newEl)