Manipulating DOM Elements
Checking access...
Once you’ve selected an element, you can change its content, appearance, structure, and behaviour. This page covers all the ways to manipulate the DOM.
Reading and Changing Content
textContent
Gets or sets the text content of an element (all child text, no HTML parsing):
<p id="message">Hello, <strong>world</strong>!</p>const p = document.getElementById("message");
// Readconsole.log(p.textContent); // "Hello, world!" (no HTML tags)
// Writep.textContent = "Goodbye!";// <p id="message">Goodbye!</p> — any HTML in the string is escapedtextContent is safe — it automatically escapes HTML tags, preventing XSS attacks.
innerHTML
Gets or sets the HTML content inside an element:
const div = document.querySelector(".content");
// Readconsole.log(div.innerHTML); // includes all HTML tags inside
// Write — parses HTML stringdiv.innerHTML = "<p>New <strong>content</strong></p>";
// Appenddiv.innerHTML += "<p>Added paragraph</p>";Danger
innerHTML with user-supplied data is a security risk. If a user types <img src=x onerror=alert('XSS')>, the script executes. Always use textContent for text, or sanitize input before using innerHTML.
innerText (vs textContent)
<p id="test" style="display: none;">Hidden text</p>const p = document.getElementById("test");
console.log(p.textContent); // "Hidden text" — includes hidden textconsole.log(p.innerText); // "" — respects CSS visibility, triggers layoutPrefer textContent — it’s faster and doesn’t trigger layout recalculations.
Working with Attributes
Standard Attributes
<a id="link" href="https://example.com" target="_blank" class="external">Link</a>const link = document.getElementById("link");
// Get attributeconsole.log(link.getAttribute("href")); // "https://example.com"console.log(link.href); // Full URL (property normalisation)
// Set attributelink.setAttribute("target", "_self");link.href = "https://other.com";
// Check existenceconsole.log(link.hasAttribute("target")); // true
// Remove attributelink.removeAttribute("target");Data Attributes
<button data-user-id="123" data-role="admin" data-active="true">Edit</button>const btn = document.querySelector("button");
// Read via dataset (camelCase)console.log(btn.dataset.userId); // "123"console.log(btn.dataset.role); // "admin"console.log(btn.dataset.active); // "true" (always string)
// Writebtn.dataset.userId = "456";btn.dataset.newAttr = "value"; // creates data-new-attr
// Checkconsole.log(btn.hasAttribute("data-user-id")); // trueManipulating Classes
<div id="box" class="card large">Content</div>const box = document.getElementById("box");
// Add classbox.classList.add("highlight");
// Remove classbox.classList.remove("large");
// Toggle (add if missing, remove if present)box.classList.toggle("active");
// Replacebox.classList.replace("card", "panel");
// Checkconsole.log(box.classList.contains("highlight")); // true
// Multiple classesbox.classList.add("visible", "animated", "fade-in");Changing Styles
Inline Styles (style property)
const el = document.querySelector(".element");
// Individual properties (camelCase)el.style.backgroundColor = "blue";el.style.fontSize = "16px";el.style.marginTop = "20px";el.style.display = "flex";
// CSS custom propertiesel.style.setProperty("--main-color", "red");
// Remove inline styleel.style.removeProperty("margin-top");
// Read computed styleconst computed = getComputedStyle(el);console.log(computed.backgroundColor);Better: Use CSS Classes
Instead of inline styles, toggle classes:
// ❌ Avoidel.style.color = "red";el.style.fontWeight = "bold";
// ✅ Preferel.classList.add("error-state"); // defined in CSS.error-state { color: red; font-weight: bold;}Creating and Inserting Elements
createElement + appendChild
// Create elementsconst div = document.createElement("div");const p = document.createElement("p");const strong = document.createElement("strong");
// Set contentp.textContent = "This is a paragraph";strong.textContent = "bold text";
// Build treep.appendChild(strong);div.appendChild(p);
// Insert into documentdocument.body.appendChild(div);Modern Insertion Methods
const parent = document.querySelector(".list");
// Append at endparent.append(newElement); // can append multiple: parent.append(el1, el2)
// Prepend at beginningparent.prepend(newElement);
// Insert before a reference elementparent.insertBefore(newElement, referenceElement);
// Insert adjacent HTMLconst target = document.querySelector(".target");target.insertAdjacentHTML("beforebegin", "<p>Before</p>");target.insertAdjacentHTML("afterbegin", "<p>First child</p>");target.insertAdjacentHTML("beforeend", "<p>Last child</p>");target.insertAdjacentHTML("afterend", "<p>After</p>");<!-- beforebegin --><div class="target"> <!-- afterbegin --> existing content... <!-- beforeend --></div><!-- afterend -->Replace and Remove
// ReplaceoldElement.replaceWith(newElement);
// Removeelement.remove(); // modernparent.removeChild(child); // legacy
// Clear all childrenelement.innerHTML = "";// orwhile (element.firstChild) { element.removeChild(element.firstChild);}Document Fragments
For bulk operations, use a DocumentFragment to avoid multiple reflows:
// ❌ Bad — triggers reflow on each appendconst list = document.getElementById("list");for (let i = 0; i < 1000; i++) { const li = document.createElement("li"); li.textContent = `Item ${i}`; list.appendChild(li); // reflow every time!}
// ✅ Good — single reflowconst fragment = document.createDocumentFragment();for (let i = 0; i < 1000; i++) { const li = document.createElement("li"); li.textContent = `Item ${i}`; fragment.appendChild(li);}list.appendChild(fragment); // single reflowClone Nodes
const original = document.querySelector(".card");
// Shallow clone — copies element but NOT childrenconst shallow = original.cloneNode(false);
// Deep clone — copies element AND all childrenconst deep = original.cloneNode(true);Checking and Comparing Elements
// Check if element is in the DOMconsole.log(document.contains(element)); // true/false
// Check if element contains anotherconsole.log(parent.contains(child)); // true/false
// Compare elementsconsole.log(element1 === element2);console.log(element1.isEqualNode(element2)); // deep equalityconsole.log(element1.isSameNode(element2)); // same referenceQuick Reference
| Operation | Method |
|---|---|
| Get/Set text | element.textContent |
| Get/Set HTML | element.innerHTML |
| Add class | element.classList.add("cls") |
| Remove class | element.classList.remove("cls") |
| Toggle class | element.classList.toggle("cls") |
| Set attribute | element.setAttribute("k", "v") |
| Get attribute | element.getAttribute("k") |
| Data attribute | element.dataset.key |
| Set style | element.style.prop = "value" |
| Create element | document.createElement("tag") |
| Append child | parent.append(child) |
| Remove | element.remove() |
| Clone | element.cloneNode(deep) |
Practice Exercises
Build a list generator: Create a page with a text input and a button. When clicked, add the input text as a new
<li>to a<ul>. Include a “Clear All” button that removes all items.Data attribute reader: Create several
<div>elements withdata-infoattributes. Write a script that reads all data attributes and displays them in a table.Class toggle demo: Create a
<div>that changes appearance when a button toggles an.activeclass. UseclassList.toggle()and define the styles in CSS.