diff --git a/packages/dom-expressions/src/client.js b/packages/dom-expressions/src/client.js index e4e6460a1..47dc6a5a2 100644 --- a/packages/dom-expressions/src/client.js +++ b/packages/dom-expressions/src/client.js @@ -460,7 +460,6 @@ function insertExpression(parent, value, current, marker, unwrapArray) { if (value === current) return current; const t = typeof value, multi = marker !== undefined; - parent = (multi && current[0] && current[0].parentNode) || parent; if (t === "string" || t === "number") { if (hydrating) return current; @@ -570,7 +569,17 @@ function appendNodes(parent, array, marker = null) { for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker); } +const moved = new Set(); +let scheduled = false; + function cleanChildren(parent, current, marker, replacement) { + if (!scheduled && moved.size) { + queueMicrotask(() => { + moved.clear(); + scheduled = false; + }) + scheduled = true; + } if (marker === undefined) return (parent.textContent = ""); const node = replacement || document.createTextNode(""); if (current.length) { @@ -579,9 +588,12 @@ function cleanChildren(parent, current, marker, replacement) { const el = current[i]; if (node !== el) { const isParent = el.parentNode === parent; - if (!inserted && !i) - isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker); - else isParent && el.remove(); + if (!inserted && !i) { + node.parentNode && moved.add(node); + isParent && !moved.has(el) + ? parent.replaceChild(node, el) + : parent.insertBefore(node, marker); + } else isParent && el.remove(); } else inserted = true; } } else parent.insertBefore(node, marker); diff --git a/packages/dom-expressions/test/dom/insert.spec.js b/packages/dom-expressions/test/dom/insert.spec.js index b4d4b2cf9..60be20a6c 100644 --- a/packages/dom-expressions/test/dom/insert.spec.js +++ b/packages/dom-expressions/test/dom/insert.spec.js @@ -2,7 +2,7 @@ * @jest-environment jsdom */ import * as r from '../../src/client'; -import * as S from "s-js"; +import S from "s-js"; describe("r.insert", () => { //
@@ -355,6 +355,54 @@ describe("r.insert with Markers", () => { expect(parent.innerHTML).toBe('hello '); }); + it("can move a node within a parent with marker", () => { + //
bar
+ //
bar
+ + const parent = document.createElement("div"); + parent.textContent = "bar"; + const marker = parent.firstChild; + + const node = document.createElement("div"); + node.textContent = "foo"; + + let current = r.insert(parent, null, marker); + let current2 = r.insert(parent, node, null); + expect(parent.outerHTML).toBe("
bar
foo
"); + + current = r.insert(parent, node, marker, current); + current2 = r.insert(parent, null, null, current2); + expect(parent.outerHTML).toBe("
foo
bar
"); + }); + + it("can move a node within parents with markers", () => { + //
bar
baz
+ //
bar
baz
+ + const bar = document.createElement("div"); + bar.textContent = "bar"; + const barMarker = bar.firstChild; + + const baz = document.createElement("div"); + baz.textContent = "baz"; + const bazMarker = baz.firstChild; + + const node = document.createElement("div"); + node.textContent = "foo"; + + let current = r.insert(bar, null, barMarker); + let current2 = r.insert(baz, node, bazMarker); + + expect(bar.outerHTML).toBe("
bar
"); + expect(baz.outerHTML).toBe("
foo
baz
"); + + current = r.insert(bar, node, barMarker, current); + current2 = r.insert(baz, null, bazMarker, current2); + + expect(bar.outerHTML).toBe("
foo
bar
"); + expect(baz.outerHTML).toBe("
baz
"); + }); + function insert(val) { const parent = container.cloneNode(true); r.insert(parent, val, parent.childNodes[1]);