diff --git a/.changeset/eager-flowers-stay.md b/.changeset/eager-flowers-stay.md new file mode 100644 index 0000000000..0e3ff95489 --- /dev/null +++ b/.changeset/eager-flowers-stay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: force the first run innerText/innerHTML/textContent written for content_editable_bind diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js index 5b10abdc4c..77259c62e4 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js @@ -1,4 +1,5 @@ import { render_effect, teardown } from '../../../reactivity/effects.js'; +import { hydrating } from '../../hydration.js'; import { listen } from './shared.js'; /** @@ -14,10 +15,12 @@ export function bind_content_editable(property, element, get, set = get) { set(element[property]); }); + var first_run = true; render_effect(() => { var value = get(); - if (element[property] !== value) { + if (element[property] !== value || (first_run && !hydrating)) { + first_run = false; if (value == null) { // @ts-ignore var non_null_value = element[property]; diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/_config.js new file mode 100644 index 0000000000..8907a84b50 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from '../../../../src/index-client'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + const h1 = /** @type {HTMLDivElement} */ (target.querySelector('h1')); + + assert.equal(h1.textContent, 'h'); + // don't use h1.textContent because h1.textContent will overwrite all childNodes + // The childNode created by {myTextBlock.current} is the reason for duplication + const textNode = + [...h1.childNodes].findLast((n) => n.nodeType === Node.TEXT_NODE) ?? + h1.appendChild(document.createTextNode('')); + textNode.nodeValue = 'he'; + h1.dispatchEvent(new window.Event('input')); + flushSync(); + + assert.equal(h1.textContent, 'he'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/main.svelte new file mode 100644 index 0000000000..f0ae61de39 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable-1/main.svelte @@ -0,0 +1,12 @@ + + + + {myTextBlock.current} + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/_config.js new file mode 100644 index 0000000000..6abade95ef --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/_config.js @@ -0,0 +1,19 @@ +import { flushSync } from '../../../../src/index-client'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + const h1 = /** @type {HTMLDivElement} */ (target.querySelector('h1')); + assert.equal(h1.textContent, ''); + // don't use h1.textContent because h1.textContent will overwrite all childNodes + // The childNode created by {myTextBlock.current} is the reason for duplication + const textNode = + [...h1.childNodes].findLast((n) => n.nodeType === Node.TEXT_NODE) ?? + h1.appendChild(document.createTextNode('')); + textNode.nodeValue = 'h'; + h1.dispatchEvent(new window.Event('input')); + flushSync(); + + assert.equal(h1.textContent, 'h'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/main.svelte new file mode 100644 index 0000000000..f067dcae23 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-contenteditable/main.svelte @@ -0,0 +1,12 @@ + + + + {myTextBlock.current} + \ No newline at end of file