mirror of https://github.com/sveltejs/svelte
fix: clean up externally-added DOM nodes in {@html} on re-render (#17853)
Fixes `{@html}` content duplication when used inside a contenteditable
element.
When `{@html content}` is inside a contenteditable element and the user
types, the browser inserts DOM nodes directly into the {@html} managed
region. On re-render (e.g. triggered by a blur handler setting `content
= e.currentTarget.innerText`, the `{@html} `block only removed nodes it
previously created via` effect.nodes`, leaving browser-inserted nodes in
place. This caused content to appear twice — once as leftover text nodes
and once as the new `{@html}` output.
The fix tracks the boundary node (`previousSibling `of the anchor at
init) and removes all nodes between the boundary and the anchor on
re-render, ensuring externally-added nodes are also cleaned up.
Closes: #16993
---------
Co-authored-by: 7nik <kfiiranet@gmail.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
pull/17877/head
parent
3df2645451
commit
0206a2019e
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: `{@html}` no longer duplicates content inside `contenteditable` elements
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { flushSync } from '../../../../src/index-client';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: `<div id="editable" contenteditable="true"></div><p id="output"></p>`,
|
||||||
|
|
||||||
|
test({ assert, target }) {
|
||||||
|
const div = /** @type {HTMLDivElement} */ (target.querySelector('#editable'));
|
||||||
|
const output = /** @type {HTMLParagraphElement} */ (target.querySelector('#output'));
|
||||||
|
|
||||||
|
// Simulate user typing by directly modifying the DOM
|
||||||
|
div.textContent = 'hello';
|
||||||
|
|
||||||
|
// Simulate blur which triggers `content = e.currentTarget.innerText`
|
||||||
|
const event = new Event('blur');
|
||||||
|
div.dispatchEvent(event);
|
||||||
|
flushSync();
|
||||||
|
|
||||||
|
// The output should show "hello" (innerText was set correctly)
|
||||||
|
assert.equal(output.textContent, 'hello');
|
||||||
|
|
||||||
|
// The contenteditable div should contain "hello" once, not duplicated
|
||||||
|
assert.htmlEqual(div.innerHTML, 'hello');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<script>
|
||||||
|
let content = $state("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="editable" onblur={(e) => { content = e.currentTarget.textContent; }} contenteditable="true">
|
||||||
|
{@html content}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="output">{content}</p>
|
||||||
Loading…
Reference in new issue