DocumentFragment
Build subtrees off-DOM with DocumentFragment — one insert, fewer reflows, and how it differs from a wrapper div.
- javascript
- documentfragment
- dom
- performance
Inserting nodes one-by-one into a live document can cost a layout/style pass each time. A DocumentFragment is a minimal document-like node that holds children off the live tree. When you insert the fragment, its children move into the target — the fragment itself does not stay as a wrapper.
Basic use
const frag = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
frag.append(li);
}
document.querySelector('#list').append(frag);
// one insertion of 1000 children; frag is now empty
frag.childNodes.length; // 0
Children are moved, not copied. After append, the fragment is empty and reusable.
Why not a temporary div?
const wrap = document.createElement('div');
// ... append lis to wrap
list.append(wrap); // leaves an extra <div> in the DOM
list.append(...wrap.childNodes); // works but live NodeList quirks
A fragment:
- Does not appear in the document
- Has no parent
- Doesn’t affect CSS selectors as a container
- Is the intended batching primitive
// template content is a DocumentFragment
const tpl = document.querySelector('#row');
const node = tpl.content.cloneNode(true);
list.append(node);
Range and fragment
const range = document.createRange();
range.selectNodeContents(list);
const taken = range.extractContents(); // DocumentFragment of former children
// mutate taken...
list.append(taken);
Useful for reordering or sanitizing a block without destroying identity of nodes you keep.
Performance reality check
Modern engines are fast; batching 20 nodes rarely needs a fragment for FPS. Fragments still win for:
- Large lists built from scratch
- Clear code intent (“build then attach”)
- Avoiding intermediate layout when reading geometry between inserts
If you interleave offsetHeight reads with inserts, a fragment alone won’t save you — stop thrashing.
With components / frameworks
Frameworks already batch DOM commits. You rarely hand-manage fragments inside React. You will still use them in:
- Vanilla widgets
- Design-system renderers
- Markdown/HTML sanitizers that build nodes
- Interview “render a list efficiently” prompts
function renderOptions(select, options) {
const frag = document.createDocumentFragment();
for (const { value, label } of options) {
const opt = document.createElement('option');
opt.value = value;
opt.textContent = label;
frag.append(opt);
}
select.replaceChildren(frag);
}
replaceChildren + fragment is a clean full refresh.
Interview answer
“DocumentFragment is an off-DOM container. I append many children to it, then append the fragment once to a live parent; children move in and the fragment empties. It avoids wrapper elements and reduces incremental layout cost for large inserts. Template.content is also a fragment.”
Related
Measuring after attach
Fragments have no layout until attached — reading geometry on nodes while only in a fragment can yield zeros depending on the node type and browser. Attach first, then measure; or use a hidden live container if you must measure offscreen.
const frag = document.createDocumentFragment();
// build...
parent.append(frag);
// now children are live — measure parent or children
const h = parent.scrollHeight;
querySelector works on a fragment for nodes you put in it (frag.querySelector('.x')), which is handy while building. After append, those nodes are gone from the fragment — hold references if you still need them.
Nested fragments and move semantics
const outer = document.createDocumentFragment();
const inner = document.createDocumentFragment();
inner.append(document.createElement('span'));
outer.append(inner); // moves span into outer; inner empties
parent.append(outer);
Appending a fragment always moves its children. That makes fragments safe builders: compose small fragments into larger ones, then attach once. Don’t keep long-lived references expecting the fragment to still contain nodes after a parent append.
Further reading
Related guides
- classList and datasetToggle CSS classes with classList and read data-* attributes via dataset — tokens, naming, and DOM performance notes.
- client, offset, and scroll DimensionsclientWidth vs offsetWidth vs scrollWidth — borders, scrollbars, and which box measurement to use for layout math.
- contentEditable PitfallsWhy contentEditable is hard: browser HTML mess, carets, sanitization, undo, and when to pick a real editor framework.
- Creating and Updating DOM NodescreateElement, textContent vs innerHTML, insert APIs, and batching updates — safe DOM writes without accidental XSS.
- Custom EventsCustomEvent, detail payloads, bubbles and composed — decoupling components without a global event bus mess.