From 2f685c1dbadcf7e158282109f746400834f5eaf5 Mon Sep 17 00:00:00 2001 From: adiGuba Date: Wed, 5 Mar 2025 20:20:56 +0100 Subject: [PATCH] fix: spreading style is not consistent with attribute (#15323) * style must be set via set_attribute * test * changeset * add empty string and null in test * explanatory comment * this is now redundant, set_attribute takes care of it * drive-by * tweak changeset --------- Co-authored-by: Rich Harris --- .changeset/real-cameras-attack.md | 5 ++ .../client/dom/elements/attributes.js | 17 ++--- .../samples/style-update/_config.js | 64 +++++++++++++++++++ .../samples/style-update/main.svelte | 9 +++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 .changeset/real-cameras-attack.md create mode 100644 packages/svelte/tests/runtime-runes/samples/style-update/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/style-update/main.svelte diff --git a/.changeset/real-cameras-attack.md b/.changeset/real-cameras-attack.md new file mode 100644 index 0000000000..35e2764785 --- /dev/null +++ b/.changeset/real-cameras-attack.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: always use `setAttribute` when setting `style` diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 44e67155fc..cebc9173ba 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -218,6 +218,7 @@ export function set_custom_element_data(node, prop, value) { // or effect var previous_reaction = active_reaction; var previous_effect = active_effect; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, // then it might run block logic in hydration mode, which we have to prevent. let was_hydrating = hydrating; @@ -227,17 +228,20 @@ export function set_custom_element_data(node, prop, value) { set_active_reaction(null); set_active_effect(null); + try { if ( + // `style` should use `set_attribute` rather than the setter + prop !== 'style' && // Don't compute setters for custom elements while they aren't registered yet, // because during their upgrade/instantiation they might add more setters. // Instead, fall back to a simple "an object, then set as property" heuristic. - setters_cache.has(node.nodeName) || + (setters_cache.has(node.nodeName) || // customElements may not be available in browser extension contexts !customElements || customElements.get(node.tagName.toLowerCase()) ? get_setters(node).includes(prop) - : value && typeof value === 'object' + : value && typeof value === 'object') ) { // @ts-expect-error node[prop] = value; @@ -378,8 +382,9 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal // @ts-ignore element[`__${event_name}`] = undefined; } - } else if (key === 'style' && value != null) { - element.style.cssText = value + ''; + } else if (key === 'style') { + // avoid using the setter + set_attribute(element, key, value); } else if (key === 'autofocus') { autofocus(/** @type {HTMLElement} */ (element), Boolean(value)); } else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) { @@ -428,10 +433,6 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal set_attribute(element, name, value, skip_warning); } } - if (key === 'style' && '__styles' in element) { - // reset styles to force style: directive to update - element.__styles = {}; - } } if (is_hydrating_custom_element) { diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/_config.js b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js new file mode 100644 index 0000000000..f0b7f2648e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js @@ -0,0 +1,64 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +const style_1 = 'invalid-key:0; margin:4px;;color: green ;color:blue '; +const style_2 = ' other-key : 0 ; padding:2px; COLOR:green; color: blue'; + +// https://github.com/sveltejs/svelte/issues/15309 +export default test({ + props: { + style: style_1 + }, + + html: ` +
+
+ + + + `, + + async test({ assert, target, component }) { + component.style = style_2; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` +
+
+ + + + ` + ); + + component.style = ''; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` +
+
+ + + + ` + ); + + component.style = null; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` +
+
+ + + + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte new file mode 100644 index 0000000000..d29590d670 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte @@ -0,0 +1,9 @@ + + +
+
+ + +