Skip to content

Commit c2dfda1

Browse files
committed
fix dynamic html bug
1 parent f3510c8 commit c2dfda1

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,51 @@
11
import { derived } from '../../reactivity/deriveds.js';
22
import { render_effect } from '../../reactivity/effects.js';
33
import { current_effect, get } from '../../runtime.js';
4+
import { is_array } from '../../utils.js';
45
import { hydrate_nodes, hydrating } from '../hydration.js';
56
import { create_fragment_from_html, remove } from '../reconciler.js';
67
import { push_template_node } from '../template.js';
78

9+
/**
10+
* @param {import('#client').Effect} effect
11+
* @param {(Element | Comment | Text)[]} to_remove
12+
* @returns {void}
13+
*/
14+
function remove_from_parent_effect(effect, to_remove) {
15+
const dom = effect.dom;
16+
17+
if (is_array(dom)) {
18+
for (let i = dom.length - 1; i >= 0; i--) {
19+
if (to_remove.includes(dom[i])) {
20+
dom.splice(i, 1);
21+
break;
22+
}
23+
}
24+
} else if (dom !== null && to_remove.includes(dom)) {
25+
effect.dom = null;
26+
}
27+
}
28+
829
/**
930
* @param {Element | Text | Comment} anchor
1031
* @param {() => string} get_value
1132
* @param {boolean} svg
1233
* @returns {void}
1334
*/
1435
export function html(anchor, get_value, svg) {
15-
var effect = anchor.parentNode !== current_effect?.dom ? current_effect : null;
36+
const parent_effect = anchor.parentNode !== current_effect?.dom ? current_effect : null;
1637
let value = derived(get_value);
1738

1839
render_effect(() => {
19-
var dom = html_to_dom(anchor, effect, get(value), svg);
20-
effect = null;
40+
var dom = html_to_dom(anchor, parent_effect, get(value), svg);
2141

2242
if (dom) {
23-
return () => remove(dom);
43+
return () => {
44+
if (parent_effect !== null) {
45+
remove_from_parent_effect(parent_effect, is_array(dom) ? dom : [dom]);
46+
}
47+
remove(dom);
48+
};
2449
}
2550
});
2651
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { flushSync } from '../../../../src/index-client';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
html: `<button>add item</button><button>make span</button><button>reverse</button>`,
6+
7+
async test({ assert, target }) {
8+
const [btn1, btn2, btn3] = target.querySelectorAll('button');
9+
10+
flushSync(() => {
11+
btn1?.click();
12+
btn1?.click();
13+
btn1?.click();
14+
});
15+
16+
assert.htmlEqual(
17+
target.innerHTML,
18+
`<button>add item</button><button>make span</button><button>reverse</button><div>Item 1</div><div>Item 2</div><div>Item 3</div>`
19+
);
20+
21+
flushSync(() => {
22+
btn2?.click();
23+
});
24+
25+
assert.htmlEqual(
26+
target.innerHTML,
27+
`<button>add item</button><button>make span</button><button>reverse</button><span>Item 1</span><span>Item 2</span><span>Item 3</span>`
28+
);
29+
30+
flushSync(() => {
31+
btn3?.click();
32+
});
33+
34+
assert.htmlEqual(
35+
target.innerHTML,
36+
`<button>add item</button><button>make span</button><button>reverse</button><span>Item 3</span><span>Item 2</span><span>Item 1</span>`
37+
);
38+
}
39+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script>
2+
let items = $state([]);
3+
4+
function add_item() {
5+
items.push({
6+
id: items.length,
7+
text: 'Item ' + (items.length + 1),
8+
html: '<div>Item ' + (items.length + 1) + '</div>',
9+
dom: null,
10+
})
11+
}
12+
13+
function make_span() {
14+
items.forEach(item => {
15+
item.html = item.html.replace(/div/g, 'span')
16+
})
17+
}
18+
19+
function reverse() {
20+
items.reverse();
21+
}
22+
</script>
23+
24+
<button on:click={add_item}>add item</button>
25+
<button on:click={make_span}>make span</button>
26+
<button on:click={reverse}>reverse</button>
27+
28+
{#each items as item (item.id)}
29+
{@html item.html}
30+
{/each}

0 commit comments

Comments
 (0)