Tutorials Logic, IN info@tutorialslogic.com

JavaScript DOM Manipulation Select, Create, Update Elements

Document Object Model

DOM manipulation reads and changes the host object tree produced from a document. Correct code distinguishes node types, query lifetimes, parsed structure, event ownership, content safety, focus behavior, and teardown.

Use scoped selectors, text-safe insertion, semantic elements, bounded rendering, and real-browser tests. Measure layout and mutation cost before optimizing, and keep domain logic independent from the DOM where practical.

JavaScript DOM Manipulation

The DOM, or Document Object Model, is the browser's object representation of an HTML page. JavaScript uses the DOM to find elements, read content, change styles, update attributes, create new elements, remove elements, and react to user actions.

If HTML is the structure and CSS is the presentation, DOM manipulation is how JavaScript changes the page after it has loaded.

Selecting Elements

Use querySelector() to select the first matching element and querySelectorAll() to select all matching elements. These methods use CSS selectors, so the same selector ideas you use in CSS work in JavaScript.

Selectors

Selectors
const heading = document.querySelector("h1");
const saveButton = document.querySelector("#saveBtn");
const cards = document.querySelectorAll(".card");

console.log(heading.textContent);
console.log(cards.length);

Changing Text and HTML

Use textContent when you want to write plain text. Use innerHTML only when you intentionally want to parse an HTML string. Never put untrusted user input into innerHTML, because that can create cross-site scripting security issues.

Text and HTML

Text and HTML
const message = document.querySelector("#message");

message.textContent = "Saved successfully";

const list = document.querySelector("#todoList");
list.innerHTML = "<li>Learn DOM</li><li>Practice events</li>";

Changing Attributes

Attributes such as href, src, alt, disabled, and aria-* can be read or updated from JavaScript. For common properties, direct property access is usually clean and readable.

Attributes

Attributes
const link = document.querySelector("#docsLink");
link.href = "https://developer.mozilla.org/";
link.textContent = "Read MDN Docs";

const image = document.querySelector("#profilePhoto");
image.src = "/images/user.png";
image.alt = "User profile photo";

const submitButton = document.querySelector("#submitBtn");
submitButton.disabled = true;
submitButton.setAttribute("aria-busy", "true");

Changing Classes and Styles

Use classList to add, remove, toggle, or check CSS classes. This is usually better than writing many inline styles because CSS remains responsible for visual design.

classList

classList
const panel = document.querySelector(".panel");

panel.classList.add("is-open");
panel.classList.remove("is-hidden");
panel.classList.toggle("is-active");

if (panel.classList.contains("is-open")) {
  console.log("Panel is visible");
}

// Use inline styles only for dynamic one-off values.
panel.style.maxHeight = "300px";

Creating and Adding Elements

Create elements with document.createElement(), set their content and attributes, then insert them with append(), prepend(), before(), or after().

Create Elements

Create Elements
const list = document.querySelector("#todoList");

const item = document.createElement("li");
item.className = "todo-item";
item.textContent = "Practice DOM manipulation";

list.append(item);

Removing and Replacing Elements

Use remove() to delete an element and replaceWith() to replace one element with another. Always check that an element exists before trying to modify it.

Remove and Replace

Remove and Replace
const oldAlert = document.querySelector(".alert");

if (oldAlert) {
  oldAlert.remove();
}

const loading = document.querySelector("#loading");
const done = document.createElement("p");
done.textContent = "Data loaded";

if (loading) {
  loading.replaceWith(done);
}

Reading Form Values

Forms are one of the most common places where DOM manipulation is used. Use value for input values, checked for checkboxes, and FormData when you want to read many fields from a form.

Forms

Forms
const form = document.querySelector("#profileForm");

form.addEventListener("submit", function (event) {
  event.preventDefault();

  const formData = new FormData(form);
  const name = formData.get("name");
  const email = formData.get("email");

  console.log({ name, email });
});

Events and DOM Updates

DOM manipulation becomes powerful when combined with events. A user clicks, types, submits, scrolls, or focuses something; JavaScript responds by updating the DOM.

Interactive Example

Interactive Example
const countText = document.querySelector("#count");
const increaseButton = document.querySelector("#increaseBtn");

let count = 0;

increaseButton.addEventListener("click", function () {
  count += 1;
  countText.textContent = count;
});

Performance Best Practices

  • Select an element once and reuse the reference when possible.
  • Prefer class changes over many direct style changes.
  • Use event delegation for long or dynamic lists.
  • Avoid repeated layout reads and writes inside tight loops.
  • Use DocumentFragment when adding many elements at once.

DocumentFragment

DocumentFragment
const list = document.querySelector("#users");
const users = ["Asha", "Ravi", "Maya", "Dev"];
const fragment = document.createDocumentFragment();

users.forEach(function (name) {
  const item = document.createElement("li");
  item.textContent = name;
  fragment.append(item);
});

list.append(fragment);

Common DOM Mistakes

  • Running JavaScript before the element exists in the page.
  • Using innerHTML with untrusted content.
  • Forgetting that querySelector() can return null.
  • Adding one event listener to every item in a very large list instead of using delegation.

DOM Tree and Node Types

The Document Object Model represents a parsed document as objects connected in a tree. `Document`, `Element`, `Text`, `Comment`, and document fragments are Node types with different capabilities. Code that needs element methods should query or narrow to Element rather than assuming every child node is an element.

HTML parsing may insert or rearrange nodes to produce a valid DOM, so the runtime tree is not always a character-for-character mirror of source markup. Inspect the Elements panel and use DOM relationships when behavior depends on actual structure.

Properties such as `children` expose elements, while `childNodes` includes text and comment nodes. Whitespace can therefore change index-based childNodes access. Prefer selectors, semantic relationships, and stable identifiers over positional assumptions.

The DOM is a host API used by JavaScript, not part of the ECMAScript language itself. Server runtimes do not provide `document` unless a library or host implements it. Keep document-dependent modules separated from reusable language and domain logic.

  • Distinguish Node, Element, Document, and Text capabilities.
  • Inspect the parsed tree rather than relying only on source markup.
  • Choose children or childNodes deliberately.
  • Separate DOM adapters from host-independent logic.

Selection and Lifecycle

`querySelector` returns the first matching Element or null, while `querySelectorAll` returns a static NodeList. Some older collection APIs return live collections that change as the document changes. Know whether iteration observes a snapshot or live mutations before editing the tree inside a loop.

Scope queries to the smallest stable component root. This prevents duplicate IDs or repeated components from selecting unrelated markup and reduces search work. Use semantic data attributes for application hooks rather than classes whose primary ownership is styling.

Initialize after required markup exists through module or deferred scripts, or from the component mount boundary. For content inserted later, initialize with the insertion operation, event delegation, or a narrowly configured observer. Re-running a global setup scan after every change creates duplicate listeners and hidden cost.

A selected element can later be detached. Delayed callbacks should verify that the component still owns the operation and, when relevant, that the node remains connected. Teardown must remove listeners, observers, timers, and references so old document subtrees can be collected.

  • Know whether a query result is static or live.
  • Scope selectors to a component root.
  • Initialize from the actual markup lifecycle.
  • Release DOM references and observers during teardown.

Safe Content and Attributes

Use `textContent` for untrusted text. `innerHTML`, `insertAdjacentHTML`, and similar parsing sinks interpret markup and can create cross-site scripting vulnerabilities when supplied with attacker-controlled content. Prefer element creation and property assignment; when rich HTML is a product requirement, sanitize with a maintained policy appropriate to the context.

Create nodes with `createElement`, set known properties or attributes, append them through fragments where useful, and preserve semantic HTML. Property and attribute values are related but not identical: form control current state, default attributes, URLs, booleans, and custom data each have specific reflection behavior.

Do not build event-handler attributes or JavaScript URLs. Register functions with event listeners and validate navigable URLs against an allowlist of protocols and origins. Browser escaping is context-specific; one generic escape operation cannot safely cover HTML, attributes, CSS, URLs, and JavaScript source.

For replacement, preserve focus, selection, scroll, and component state where required. Replacing a container with innerHTML destroys descendant nodes and listeners. A targeted update or rendering system with stable identity can avoid unexpected teardown.

  • Insert untrusted strings through textContent.
  • Use context-specific validation for URLs and attributes.
  • Register functions instead of inline handler source.
  • Preserve focus and identity during replacement.

Mutation and Rendering Performance

DOM changes can invalidate style and layout. Reading geometry after a write may force the browser to update layout immediately. Group reads before writes, avoid alternating measurement and mutation in loops, and profile before introducing complex batching.

Use a DocumentFragment or build a detached subtree when many nodes are appended together, but measure the real bottleneck. Algorithmic work, image decoding, event volume, network delay, and framework rendering can dominate the DOM operation itself.

MutationObserver reports batches of tree, attribute, or text changes after mutations occur. Configure the narrowest root and observation options, disconnect at teardown, and avoid observers that mutate the same attributes without a guard. An observer is not a substitute for calling component code directly when the mutation is already owned.

Large collections need bounded rendering. Window or paginate rows when the product permits, preserve keyboard and screen-reader behavior, and avoid replacing the entire list for one changed item. Use stable identifiers so updates target the correct node after sorting or filtering.

  • Batch layout reads separately from DOM writes.
  • Profile before applying micro-optimizations.
  • Scope and disconnect MutationObservers.
  • Render large collections with stable identity and bounded work.

Accessible DOM Updates and Tests

Start with semantic elements so native keyboard, focus, form, and accessibility behavior is available before scripting. A clickable div is not equivalent to a button. If a custom widget is necessary, implement its full keyboard and focus pattern and expose state with appropriate accessible semantics.

When content changes, move focus only when the workflow requires it and never leave focus in a removed subtree. Status messages that must be announced need an appropriate live region established before the update. Avoid flooding assistive technology with every keystroke or progress tick.

Test selection failure, empty content, repeated mounting, teardown, nested event targets, keyboard operation, focus retention, unsafe strings, and large updates. DOM-emulation unit tests are fast, but real-browser tests are needed for parsing, layout, focus, events, and accessibility-tree behavior.

Use developer-tool breakpoints for subtree modification, attribute changes, or node removal when ownership is unclear. Record component and operation IDs in temporary diagnostics, then remove noisy observers. Regression tests should assert the visible and accessible result, not private mutation order alone.

  • Build behavior on semantic elements first.
  • Manage focus and announcements during dynamic updates.
  • Combine DOM unit tests with real-browser coverage.
  • Assert visible and accessible outcomes.
Before you move on

JavaScript DOM Manipulation Select, Create, Update Elements Mastery Check

5 checks
  • Scope selectors and handle nullable lookup results.
  • Use textContent for untrusted text.
  • Preserve semantic behavior, focus, and accessible announcements.
  • Batch measured DOM work and disconnect observers.
  • Test repeated mount, teardown, keyboard use, and unsafe input.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.