You move a live subtree into a different container by appending it there. The nodes are not re-created, so what state is still lost, and how do you protect what matters?
answer
- identity survives, rendered state does not
- focus is dropped on removal
- scroll offsets belong to the layout box
- iframes discard their browsing context
- move the box with CSS, not the node
basics
~20 sAnything the browser derives from the node being in a rendered document is discarded: focus, text selection, a scrollable element's offset, running CSS transitions and animations, and an iframe's loaded document, which reloads. Save that state before the move and restore it after.
solid answer
~40 sA move preserves node identity, so listeners, attributes and expando properties survive — but any state the browser derives from the element being *in a document* does not. Removal blurs the focused element and focus does not come back on its own; text selection collapses; a scrolled container's `scrollTop` resets because its layout box was destroyed; running CSS transitions and animations restart; and an `<iframe>` is the expensive one, because detaching discards its browsing context, so re-inserting reloads the page from `src` and throws away everything inside it. The mitigations are explicit: capture `document.activeElement` plus `selectionStart`/`selectionEnd` and `scrollTop` before the move, restore them after, and for iframes or media do not re-parent at all — reposition with CSS instead, or use an API designed for it such as the fullscreen or Picture-in-Picture APIs.
code
javascript · 16 linesconst panel = document.querySelector('#panel');
const target = document.querySelector('#modal-body');
// capture document-derived state before the move
const wasFocused = panel.contains(document.activeElement)
? document.activeElement : null;
const scrollTop = panel.scrollTop;
target.append(panel); // detach + reinsert
console.log(panel.scrollTop); // 0 - layout box was rebuilt
console.log(document.activeElement); // <body> - focus was released
// restore it explicitly
panel.scrollTop = scrollTop;
if (wasFocused) wasFocused.focus();go deeper
Know that moving an element keeps the element and its listeners but that the page can still visibly change — focus is lost and a scrolled area jumps back to the top.
Explain why: the node object survives but its layout box and document-derived state are destroyed on removal, so scroll offsets, focus, selection and running animations reset when it is re-inserted.
Show the operational answer — capture focus, selection and scroll before the move and restore after — and name the case you cannot fix, an iframe whose browsing context is discarded and reloads.
Argue the design position: state-heavy subtrees should never be re-parented, and requirements that look like moves are usually visual, so reach for CSS positioning, the top layer, or a purpose-built API before restructuring the tree.
## Node identity survives; document-derived state does not Moving a subtree — `newParent.append(subtree)` — is a single detach-and-reinsert. The node objects are untouched, so everything stored *on* them comes along: `addEventListener` listeners, attributes and `dataset`, custom JavaScript properties, and the whole descendant tree. What does not come along is everything the browser computes *because* the node sits in a rendered document. That state is torn down at removal and rebuilt fresh at insertion. Enumerating it is the substance of this question. ## The list **Focus.** When the focused element is removed from the document, focus is released and lands back on the body. Re-inserting the element does not re-focus it. If the user was typing when your code reparented the field, the caret vanishes mid-keystroke. **Text selection.** The selection is anchored to nodes in the document; removing them collapses it. Restoring means re-establishing a `Range`, and for form fields, re-applying `selectionStart` and `selectionEnd`. **Scroll offsets.** A scrollable element's `scrollTop` and `scrollLeft` are properties of its layout box, and that box is destroyed on removal. On re-insertion it is recreated scrolled to the top. This bites hardest when the moved subtree *contains* a scrolled region, because it is easy to forget the inner one. **CSS transitions and animations.** Leaving the document ends them. A CSS animation restarts from its first keyframe on re-insertion rather than resuming, and a transition in flight is simply abandoned. **Iframes.** This is the one people get wrong. An `<iframe>`'s document is not stored on the element; it belongs to a nested browsing context that is discarded when the element is detached. Re-inserting the iframe starts a fresh load from its `src`, so the framed page reloads: form input, scroll position, session state in that page and any script running inside it are gone. There is no flag to prevent it — moving an iframe in the DOM means reloading it. **Media elements.** `<video>` and `<audio>` are a subtler case. The HTML specification's rule is that removal schedules a check that runs after the current task settles, and pauses playback only if the element is *still* out of the document at that point. So a synchronous remove-and-reinsert within one task typically keeps playing, while an asynchronous move — detach now, insert in a later task or after an `await` — pauses it. ## Protecting what matters There is no automatic mechanism, so the pattern is capture, move, restore: ```js function moveWithState(node, newParent) { const active = node.contains(document.activeElement) ? document.activeElement : null; const selStart = active && 'selectionStart' in active ? active.selectionStart : null; const selEnd = active && 'selectionEnd' in active ? active.selectionEnd : null; const scrollers = [...node.querySelectorAll('*')] .filter(el => el.scrollTop || el.scrollLeft) .map(el => [el, el.scrollTop, el.scrollLeft]); newParent.append(node); for (const [el, top, left] of scrollers) { el.scrollTop = top; el.scrollLeft = left; } if (active) { active.focus(); if (selStart !== null) active.setSelectionRange(selStart, selEnd); } } ``` That handles focus, selection and scroll. Animations and iframes it cannot help with, which points at the more important lesson. ## Design around the move instead The robust answer in an interview is that state-heavy subtrees should not be re-parented at all: - **Move the box, not the node.** A great many reparenting requirements — promote this panel into a modal, dock this player to the corner — are really visual requirements. `position: fixed`, a transform, or the top layer via `<dialog>` and `showModal()` achieve them without touching the tree, so nothing is destroyed. - **Use the purpose-built API.** For "show this video full-screen" or "pop it out", the Fullscreen API and Picture-in-Picture exist precisely so the element never changes parents. - **Render both places, own the state elsewhere.** If the DOM must differ, keep authoritative state in JavaScript and rebuild the view, rather than shuttling live nodes around and hoping their implicit state survives. ## Why interviewers like this It separates people who know that a move preserves listeners — a good fact — from people who have shipped a reparenting feature and discovered that a preserved node is not preserved *state*. The distinction between object identity and document-derived state is the whole answer.
- Is there any way to move an iframe in the DOM without reloading it?No. The iframe's document lives in a nested browsing context that is discarded when the element is detached, and every insertion is preceded by a detach, so re-insertion always starts a fresh load from `src`. The workaround is not to move it: reposition the iframe visually with CSS, or restructure so it is created once in a container that never changes parents.
- A user is typing in a text field when your code reparents its container. What exactly do you have to restore?Focus and the caret. Capture `document.activeElement` before the move and, for a text control, its `selectionStart` and `selectionEnd`. After re-inserting, call `focus()` and then `setSelectionRange(start, end)`. Neither is restored automatically, because removal from the document releases focus and collapses the selection.
- Does a <video> stop playing when you move it to another container?Usually not, if the detach and re-insert happen in the same task. The specification pauses a removed media element only after checking, once the current task settles, that it is still out of the document — so a synchronous move slips through. Split the move across a timeout or an await and playback pauses, which makes it a genuinely fragile thing to rely on.
- When would you reparent anyway, despite all of this?When the subtree is stateless markup and the alternative is worse — for example reordering rows in a list, where moving preserves listeners and avoids re-rendering. The heuristic is that moving is cheap and safe for presentational nodes, and risky exactly in proportion to how much implicit browser state the subtree holds.
saying these in an interview costs you the question
- Assuming a moved node keeps focus and scroll position
- Believing an iframe survives being re-parented
- Thinking a CSS animation resumes rather than restarts
- Confusing preserved listeners with preserved state
- Reparenting to achieve a purely visual reposition