We're excited to announce the new moveBefore()
DOM API, available in Chrome version 133, that makes it easier to move elements around in the DOM without losing state. Keep reading to find out how you can use it in your projects!
Losing state during DOM mutations
Do you use the appendChild()
API to insert new elements into the DOM? So do many, but have you ever tried calling it—or insertBefore()
, or any other insertion API for that matter—with an element that's already in the DOM? If so, you might not have realized that this quietly works by first removing the element from its old parent, and re-inserting it into the new one. That's because the Document Object Model has only had the remove and insert primitives since the first DOM Standard draft was introduced in 1998. Whenever you think you're "moving" something around in the DOM from one place to another, you're really removing and inserting under the hood.
The fact that a "move" is really a "remove and insert" usually has no impact on user experience. For example, when "moving" a <p>
around in the DOM, these two operations have no disruptive side effects, but when moving complex nodes that hold significant state—like <iframe>
elements, elements in fullscreen, CSS animations, and so on—the implicit "removal" operation resets all sorts of state.
This can have surprisingly disruptive side effects
You can see the kind of state that gets reset in out state-preserving demo website by playing around with moves in the DOM tree. The following example shows CSS animations and <iframe>
state resetting when moving elements from one parent container to another.
This limitation can make building dynamic user experiences difficult or even impossible. Users get frustrated and confused when application state mysteriously gets reset, and JavaScript framework authors bear the brunt of this by spending countless hours rearchitecting their frontend code around this issue, authoring complex libraries like MorphDOM, or by fielding bug reports that highlight issues they can't fix.
The new moveBefore()
API
We set out to fix this problem by adding a new primitive operation to the DOM. It's appropriately called the "move" primitive, and is exposed to developers through the new moveBefore()
DOM API.
moveBefore()
takes the same arguments as insertBefore()
, but instead of removing and re-inserting nodes when they're already attached to the DOM, this new API atomically moves the target node into the new parent without resetting most state. This finally lets JavaScript developers build dynamic experiences with movable animations, iframes, fullscreen elements, and more. You can try this out for yourself by enabling the chrome://flags/#atomic-move
experimental flag and visiting our demo site, or by using version 133 of Chrome after its release on February 4th, 2025.
Examples of behaviors that this new primitive will allow JavaScript authors to achieve are:
- Preserve a video's play state as a user navigates through a website (whether the video is provided from a
<video>
or<iframe>
element). - Preserve the focus of a user input field as it is moved in the DOM.
- Allow animations to finish smoothly as new content is added or removed from the DOM.
- Higher fidelity morphing algorithms for reconciling existing DOMs with new content.
- Keep modal dialogs, popovers and fullscreen elements open.
We're working hard on introducing this API to the web platform with other browsers, and we're thrilled to get it into developers' hands soon, satisfying years of developer requests and filling in a significant gap in the web platform.
As always, let us know what you think via Twitter or the comments below, and submit bugs to crbug.com/new.