Select, modify, create, and delete HTML elements entirely through JavaScript — the techniques that power every interactive page on the web.
DOM Manipulation refers to the process of selecting HTML elements and dynamically modifying their content, attributes, styles, or structure using JavaScript.
With DOM manipulation you can change text after a button click, apply styles in response to user input, build new elements from data, and remove elements that are no longer needed — all without touching the HTML file or reloading the page.
DOM Manipulation means "control your webpage using JavaScript". Four operations cover everything:
<h1 id="title">Hello</h1>
<p class="note">First note</p>
<p class="note">Second note</p>
<script>
// By ID — returns one element
let h = document.getElementById("title");
console.log(h.textContent); // "Hello"
// querySelector — returns FIRST match (CSS selector)
let first = document.querySelector(".note");
console.log(first.textContent); // "First note"
// querySelectorAll — returns ALL matches (NodeList)
let all = document.querySelectorAll(".note");
console.log(all.length); // 2
</script>
getElementById(id) — fastest selector, returns one element by its idquerySelector(css) — flexible, returns the first element matching any CSS selectorquerySelectorAll(css) — returns a NodeList of all matches, iterable with forEach<h2 id="heading">Original Heading</h2>
<img id="logo" src="old.png" alt="Logo">
<script>
let el = document.getElementById("heading");
// Change text
el.textContent = "Updated Heading";
// Change inline style
el.style.color = "#f7df1e";
el.style.background = "#111";
el.style.padding = "8px 16px";
// Change attribute
document.getElementById("logo").setAttribute("src", "new.png");
document.getElementById("logo").setAttribute("alt", "New Logo");
</script>
element.style.propertyName sets inline CSS — camelCase (backgroundColor, not background-color)setAttribute(name, value) sets any HTML attribute — src, href, class, altgetAttribute(name) reads an attribute's current value<ul id="myList">
<li>Item 1</li>
</ul>
<script>
let list = document.getElementById("myList");
// 1. Create the element
let newItem = document.createElement("li");
// 2. Set its content
newItem.textContent = "Item 2 — added by JavaScript";
// 3. Append it to the parent
list.appendChild(newItem);
// Modern alternative: insertAdjacentHTML
list.insertAdjacentHTML("beforeend", "<li>Item 3 — via insertAdjacentHTML</li>");
</script>
createElement(tag) creates a new detached element — it is not visible until you insert itappendChild(element) inserts it as the last child of the parentinsertAdjacentHTML(position, html) is a faster shortcut when you have an HTML string ready<ul id="items">
<li id="a">Keep me</li>
<li id="b">Delete me</li>
<li id="c">Keep me too</li>
</ul>
<script>
// Modern way — element removes itself
document.getElementById("b").remove();
// Classic way — parent removes child
// let list = document.getElementById("items");
// let target = document.getElementById("b");
// list.removeChild(target);
</script>
element.remove() is the modern, clean way — the element removes itself from the DOMparent.removeChild(child) is the older equivalent — requires a reference to both parent and childThink of a remote control for your TV — every button maps to a DOM operation:
Click ▶ Preview to see all four DOM operations — select, modify, add, and remove — working live in one demo.
<!DOCTYPE html>
<html>
<head><title>DOM Demo</title></head>
<body style="font-family:sans-serif;padding:24px;background:#f8f9fa">
<h2 id="heading">Hello Aman</h2>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px">
<button onclick="changeText()"
style="padding:7px 14px;background:#f7df1e;border:none;border-radius:6px;
cursor:pointer;font-weight:700">Change Text</button>
<button onclick="addItem()"
style="padding:7px 14px;background:#4ade80;border:none;border-radius:6px;
cursor:pointer;font-weight:700">Add Item</button>
<button onclick="removeItem()"
style="padding:7px 14px;background:#f87171;border:none;border-radius:6px;
cursor:pointer;font-weight:700;color:#fff">Remove Item</button>
</div>
<ul id="myList" style="padding-left:20px;line-height:2">
<li>Item 1</li>
</ul>
<script>
let count = 1;
function changeText() {
let h = document.getElementById("heading");
h.textContent = "Text Changed by JavaScript!";
h.style.color = "#e07b00";
}
function addItem() {
count++;
let li = document.createElement("li");
li.textContent = "Item " + count;
document.getElementById("myList").appendChild(li);
}
function removeItem() {
let list = document.getElementById("myList");
if (list.lastElementChild) {
list.lastElementChild.remove();
if (count > 1) count--;
}
}
</script>
</body>
</html>
<h2 id="myHeading">Original Heading</h2>
<button onclick="updateIt()">Update</button>
<script>
function updateIt() {
// select #myHeading and change its text
}
</script>
getElementById("myHeading") to select the headingtextContent to "Updated by JS!"style.color to "blue"<ul id="taskList"></ul>
<button onclick="addTask()">Add Task</button>
<button onclick="removeTask()">Remove Last</button>
<script>
let n = 0;
function addTask() {
// create <li>, set text, append to #taskList
}
function removeTask() {
// remove the last <li> from #taskList
}
</script>
addTask(): increment n, create a <li>, set text to "Task N", append itremoveTask(): call list.lastElementChild?.remove()id?
getElementById() is the fastest selector — it looks up a single unique element by its id attribute. querySelector("#id") does the same thing but is slightly slower due to CSS selector parsing.
querySelector(".card") return?
<p class="card">First</p>
<p class="card">Second</p>
<script>
let el = document.querySelector(".card");
console.log(el.textContent);
</script>
querySelector() returns only the first matching element in document order. To get all matches use querySelectorAll(), which returns a NodeList you can loop through.
let li = document.createElement("li");
li.textContent = "New item";
document.querySelector("ul").appendChild(li);
createElement(tag) creates a new detached element that doesn't appear on the page yet. appendChild() then inserts it. You always need both — create first, then attach.
Prefer querySelector and querySelectorAll over the older selection methods — they accept any CSS selector and work consistently:
// Old API — limited and inconsistent
document.getElementById("title");
document.getElementsByClassName("card"); // live HTMLCollection
document.getElementsByTagName("p"); // live HTMLCollection
// Modern API — flexible, consistent, preferred
document.querySelector("#title"); // by ID
document.querySelector(".card"); // by class (first match)
document.querySelectorAll("p.note"); // all <p> with class "note"
document.querySelector("ul li:last-child"); // any CSS selector!
Use element.remove() — the modern self-removal method. The old parent.removeChild(child) requires an extra reference to the parent:
// Modern — element removes itself (clean)
document.querySelector("#item2")?.remove();
// Old — must go through the parent
let parent = document.querySelector("#myList");
let child = document.querySelector("#item2");
if (child) parent.removeChild(child);
Use classList to add, remove, and toggle CSS classes — far cleaner than manually editing style properties:
let el = document.querySelector("#heading");
el.classList.add("highlight"); // add class
el.classList.remove("highlight"); // remove class
el.classList.toggle("active"); // add if absent, remove if present
console.log(el.classList.contains("active")); // true or false
Batch DOM writes — every direct DOM change can trigger a browser repaint. Build your elements in memory first, then insert the whole structure at once:
let list = document.querySelector("#output");
let fragment = document.createDocumentFragment(); // off-screen container
["Alpha", "Beta", "Gamma"].forEach(name => {
let li = document.createElement("li");
li.textContent = name;
fragment.appendChild(li); // no repaint yet
});
list.appendChild(fragment); // one repaint, three items inserted
A list item is created and given text — but will it ever appear on the page? Think about what's missing before checking in the preview.
<ul id="list">
<li>Existing item</li>
</ul>
<script>
let li = document.createElement("li");
li.textContent = "New Item";
// Nothing else...
</script>
// Step 1: document.createElement("li")
// Creates a new <li> element in memory
// It exists as a JavaScript object — but NOT in the DOM yet
// It is completely detached from the page
// Step 2: li.textContent = "New Item"
// Sets the text of the in-memory element
// Still NOT attached to the page
// Step 3: Nothing else...
// The element is created and configured but NEVER inserted
// The browser only renders elements that are IN the DOM
// Answer: NO — "New Item" will NOT appear on screen.
// Only "Existing item" is visible because it was in the HTML.
// Fix: you must append the element to the DOM
// document.getElementById("list").appendChild(li);
// ← After this line, "New Item" appears on the page
// Key rule:
// createElement() → creates in memory (invisible)
// appendChild() / insertAdjacentHTML() → puts it in the DOM (visible)