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