Skip to content

Commit 0ad0f04

Browse files
authored
fix: more robust moving of each item nodes (#11254)
* move then link * fix: more robust moving of each item nodes * test
1 parent a8ca2a4 commit 0ad0f04

File tree

4 files changed

+75
-18
lines changed

4 files changed

+75
-18
lines changed

.changeset/brave-carrots-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: more robust moving of each item nodes

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -323,26 +323,25 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
323323
if (matched.length < stashed.length) {
324324
// more efficient to move later items to the front
325325
var start = stashed[0];
326-
var local_anchor = start.o;
327326
var j;
328327

329328
prev = start.prev;
330329

331330
var a = matched[0];
332331
var b = matched[matched.length - 1];
333332

334-
link(a.prev, b.next);
335-
link(prev, a);
336-
link(b, start);
337-
338333
for (j = 0; j < matched.length; j += 1) {
339-
move(matched[j], local_anchor);
334+
move(matched[j], start, anchor);
340335
}
341336

342337
for (j = 0; j < stashed.length; j += 1) {
343338
seen.delete(stashed[j]);
344339
}
345340

341+
link(a.prev, b.next);
342+
link(prev, a);
343+
link(b, start);
344+
346345
current = start;
347346
prev = b;
348347
i -= 1;
@@ -352,7 +351,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
352351
} else {
353352
// more efficient to move earlier items to the back
354353
seen.delete(item);
355-
move(item, current ? current.o : anchor);
354+
move(item, current, anchor);
356355

357356
link(item.prev, item.next);
358357
link(item, prev.next);
@@ -492,21 +491,19 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
492491

493492
/**
494493
* @param {import('#client').EachItem} item
494+
* @param {import('#client').EachItem | null} next
495495
* @param {Text | Element | Comment} anchor
496496
*/
497-
function move(item, anchor) {
498-
anchor.before(item.o);
497+
function move(item, next, anchor) {
498+
var end = item.next ? item.next.o : anchor;
499+
var dest = next ? next.o : anchor;
499500

500-
var dom = item.e.dom;
501+
var node = /** @type {import('#client').TemplateNode} */ (item.o);
501502

502-
if (dom !== null) {
503-
if (is_array(dom)) {
504-
for (var i = 0; i < dom.length; i++) {
505-
anchor.before(dom[i]);
506-
}
507-
} else {
508-
anchor.before(dom);
509-
}
503+
while (node !== end) {
504+
var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling);
505+
dest.before(node);
506+
node = next_node;
510507
}
511508
}
512509

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { flushSync } from 'svelte';
2+
import { ok, test } from '../../test';
3+
4+
let ascending = `
5+
<button>reverse</button>
6+
<p>1</p>
7+
<p>(1)</p>
8+
<p>2</p>
9+
<p>(2)</p>
10+
<p>3</p>
11+
<p>(3)</p>
12+
`;
13+
14+
let descending = `
15+
<button>reverse</button>
16+
<p>3</p>
17+
<p>(3)</p>
18+
<p>2</p>
19+
<p>(2)</p>
20+
<p>1</p>
21+
<p>(1)</p>
22+
`;
23+
24+
export default test({
25+
html: ascending,
26+
27+
async test({ assert, target }) {
28+
const btn = target.querySelector('button');
29+
ok(btn);
30+
31+
flushSync(() => btn.click());
32+
assert.htmlEqual(target.innerHTML, descending);
33+
34+
flushSync(() => btn.click());
35+
assert.htmlEqual(target.innerHTML, ascending);
36+
37+
flushSync(() => btn.click());
38+
assert.htmlEqual(target.innerHTML, descending);
39+
40+
flushSync(() => btn.click());
41+
assert.htmlEqual(target.innerHTML, ascending);
42+
}
43+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
let array = $state([1, 2, 3]);
3+
</script>
4+
5+
<button onclick={() => array.reverse()}>reverse</button>
6+
7+
{#each array as item (item)}
8+
<p>{item}</p>
9+
{#if true}
10+
<p>({item})</p>
11+
{/if}
12+
{/each}

0 commit comments

Comments
 (0)