JavaScript DOM Manipulation Table

APIWhat it doesField note
createElement + textContentBuild a node safelytextContent never parses - the XSS-proof way to add user text
append / prependInsert nodes OR stringsAccepts multiple arguments; strings become text nodes
appendChildThe classic insertMOVES an existing node (no copy) - reordering lists for free
insertAdjacentHTMLParse HTML at 4 positionsFaster than node loops; NEVER feed it user input
replaceChildren()Wipe and fill in one callThe modern rerender - replaces the innerHTML='' + append dance
cloneNode(true)Deep copyClones attributes and children, NOT event listeners
remove()Self-deletenode.remove() retires parent.removeChild(node)
DocumentFragmentBatch off-screenBuild 100 nodes, insert once - one reflow instead of a hundred
Reference: the MDN createElement reference. DOM manipulation splits on one axis: parsed HTML versus built nodes. textContent and createElement never parse strings, so user input stays inert text; innerHTML and insertAdjacentHTML parse everything, so user input becomes executable markup - that single distinction IS frontend XSS hygiene. Bottom line: replaceChildren() is the modern rerender (one call, no stale-node cleanup), appendChild MOVES nodes so reordering is free, and cloneNode spares listeners - which is exactly why event delegation on the parent survives list rebuilds. Related tools: DOM traversal table (finding the nodes first), security headers table (CSP as the second XSS dam), and closure table (why detached nodes leak via handlers).

DOM manipulation splits on one axis: parsed HTML versus built nodes. textContent and createElement never parse strings, so user input stays inert text; innerHTML and insertAdjacentHTML parse everything they touch, so user input becomes executable markup - that single distinction is frontend XSS hygiene.

Bottom line: replaceChildren() is the modern rerender - one call wipes and fills, replacing the innerHTML = '' plus append dance. appendChild MOVES existing nodes rather than copying, which makes reordering free, and cloneNode spares event listeners - which is exactly why event delegation on the parent survives list rebuilds.

The honest part: layout thrash is the other cost axis. Appending one hundred nodes one by one can trigger a reflow per insert; building them inside a DocumentFragment and inserting once collapses it to a single layout pass - same nodes, one reflow.

How to use

  1. Render user text safely: const el = document.createElement('div'); el.textContent = userInput - no parsing means no script execution, ever.
  2. Rerender a list cleanly: parent.replaceChildren(...newNodes) - built in a fragment, inserted in one call, no stale-node cleanup loop.
  3. Batch heavy inserts: append nodes to a DocumentFragment first, then parent.append(fragment) - one reflow for the whole batch.

Frequently asked questions

When is insertAdjacentHTML acceptable, given innerHTML's risks?

For templates YOU control - static markup with your own interpolations escaped, or content fetched from a trusted build. Its advantage is real: it parses once at the insertion point (beforebegin, afterbegin, beforeend, afterend) without re-serializing the container, and it is faster than createElement loops for large static blobs. The hard rule is the data boundary: any string that contains user input goes through textContent or a sanitizing step first. Teams that can't hold the boundary consistently should default to createElement everywhere and never parse.

Why does appendChild move a node instead of duplicating it?

The DOM is a tree of node OBJECTS, and a node can live at exactly one position - appending an existing node atomically removes it from its old parent and inserts it at the new one. That is a feature: reordering is parent.append(child) with no removal step, and moving between containers is just as direct. To genuinely duplicate, say so with cloneNode(true) - and remember the clone carries attributes and children but NOT event listeners, because listeners live in the event system, not on the node's data.

What problem does DocumentFragment actually solve?

Reflow batching. Each append to a live, in-document element can dirty layout; a hundred appends can mean a hundred recalculations, visibly stuttering on big lists. A DocumentFragment is an off-screen node: appends into it cost nothing user-visible, and a single append(fragment) transfers all children into the document at once - one layout pass. Modern engines batch some of this automatically, but the fragment remains the reliable contract, especially when interleaving reads and writes would otherwise force synchronous reflows.

How does event delegation interact with DOM manipulation?

Listeners attach to nodes; replace the nodes and the listeners die with them. Delegation attaches ONE listener to a stable parent and inspects event.target on each event - children can be created, moved and destroyed freely, and the handler keeps working. That is why rerender-heavy UIs (lists, tables, chat feeds) delegate from the container instead of binding per item: fewer listeners, no leak, and replaceChildren() rebuilds the content without touching the wiring. cloneNode's listener-stripping becomes irrelevant when no listeners live on the clones in the first place.

Related tools