Repository navigation
Proposal: moveOrInsertBefore() #1406
Description
What problem are you trying to solve?
In adding support for moveBefore() to libraries, we have to add checks that mirror the Move operation in the spec. Something like:
// Do once per range of sibling nodes that need to be moved
const sameRoot = oldParent.getRootNode({composed: true}) === newParent.getRootNode({composed: true});
// This conditional attempts to re-implement the precondition checks that
// the Move operation has in addition to those the Insert operation has in the DOM spec
const moveMethod = sameRoot && (node.nodeType === 1 || node.nodeType === 3 || node.nodeType === 8)
? container.moveBefore
: container.insertBefore;
moveMethod.call(container, node, refNode);This is a bit tricky to get right because what preconditions you check depends on which are invariants for the particular type of move we're doing (many moves are within the same parent for instance, but some are not), and because the conditions are more complex than for methods like insertBefore().
It also is redundant, as moveBefore() is already performing these checks, just to throw. In fact, we could do:
try {
container.moveBefore(node, refNode);
} catch (e) {
container.insertBefore(node, refNode);
}but since moveBefore already knows whether we can use an atomic move or not, why can't we have a method that just does this check and fallback for us? IOW, we know we want to move the node - there's no exception where we decide to just not move it - and we want to preserve state if possible, so do that with as little ceremony as possible:
container.moveOrInsertBefore(node, refNode);Frameworks and libraries really don't want to have to carry around extra code for checks the DOM could do, or perform redundant checks that the DOM is already doing.
What solutions exist today?
No response
How would you solve it?
No response
Anything else?
Bulk mutation methods would help reduce repeated checks even in the DOM for ranges of nodes: #1369
Activity
smaug---- commentedon Oct 6, 2025
The example code is quite unclear. sameroot isn't used for anything, as an example.
What is the problematic case you're trying to handle?
moveOrInsertBefore certainly does smell a bit like a kitchen sink method ;)
justinfagnani commentedon Oct 7, 2025
@smaug---- sorry I was trying to simplify my actual code to make it more readable here. I'll clean it up.
The point is that in order to safely use
moveBefore()we have to reimplement the checks that it does and fall back toinsertBefore()if our nodes don't meet the requirements. It would be a lot easier, and likely faster, if there was a DOM API that just did this for us. I don't see the benefit in forcing this conditional logic to be wrapped around so many `moveBefore() calls.justinfagnani commentedon Oct 7, 2025
noamr commentedon Oct 15, 2025
This version is probably sufficient for any practical use (unless you really need the runtime is-element nodeType checks):
or:
Some of the other premutations like moving
CharacterDataor moving elements in an inactive document are not that interesting if you're anyway falling back toinsertBefore- those are cases wheremoveBeforewould "succeed" but have the same effect asinsertBefore.I don't mind having this as part of the platform (and it was discussed before) but the polyfill is perhaps not that bad?
keithamus commentedon Oct 15, 2025
We should consider the performance constraints of JS code like that. It's been a while for me looking at stuff like this but
try/catchin a hot path could impact perf. If on the implementation side we're just checking a flag, then it might be enough to provide a reasonable justification to add to the platform - but I think emperical evidence is necessary.noamr commentedon Oct 15, 2025
That's just one of the options. The first one was just checking a couple of flags.
trusktr commentedon Sep 18, 2026
I strongly agree with Justin here.
The thing is, it isn't the consumer of the APIs that should care about how elements/nodes handle their own state when moved.
Consumers of this API (or any API that lets them move a child) just want to move the element/node.
It is the responsibility of the moved element/node itself to determine what to do, exactly like custom elements can with and without
connectedMoveCallback.On the other hand, native nodes throwing an error saying "I cannot be moved" is totally nonsensical to the consumer.
This is an unnecessary leak of internal implementation detail.
Consumer (even a framework's) thought process:
"I'm gonna move this node" -> error -> "WTF?" -> tries other method -> it works -> "WTF?"
(Where F stands for fudge cakes, of course 😊)
It is
When we want to move a node, just let us move the node.
(Parent elements that care about childList changing observe children, which is not relevant to this.)
trusktr commentedon Sep 19, 2026
Another question is, what can spec authors do to get more of this sort of feedback before finalizing spec?
annevk commentedon Sep 19, 2026
I think we just disagree. Moving is not an implementation detail. If you think it should be, I recommend not using the move primitive at all and always use the APIs that disconnect and connect.
smaug---- commentedon Sep 21, 2026
moveBefore is vastly different primitive to insertBefore. If those were the same method, then we'd need to know which path is taken. Say, for example if there is an iframe around, with move no document is destroyed and loaded, but with insert both happen, and now one might need to wait for the load event to fire before working on the iframe again.