fix: set strings as attributes, non-strings as properties if property exists

pull/13327/head
Rich Harris 2 years ago
parent d65304007d
commit 2601da9cf9

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: set strings as attributes, non-strings as properties if property exists

@ -81,8 +81,6 @@ export function set_checked(element, checked) {
* @param {boolean} [skip_warning] * @param {boolean} [skip_warning]
*/ */
export function set_attribute(element, attribute, value, skip_warning) { export function set_attribute(element, attribute, value, skip_warning) {
value = value == null ? null : value + '';
// @ts-expect-error // @ts-expect-error
var attributes = (element.__attributes ??= {}); var attributes = (element.__attributes ??= {});
@ -95,7 +93,7 @@ export function set_attribute(element, attribute, value, skip_warning) {
(attribute === 'href' && element.nodeName === 'LINK') (attribute === 'href' && element.nodeName === 'LINK')
) { ) {
if (!skip_warning) { if (!skip_warning) {
check_src_in_dev_hydration(element, attribute, value); check_src_in_dev_hydration(element, attribute, value ?? '');
} }
// If we reset these attributes, they would result in another network request, which we want to avoid. // If we reset these attributes, they would result in another network request, which we want to avoid.
@ -113,12 +111,17 @@ export function set_attribute(element, attribute, value, skip_warning) {
element[LOADING_ATTR_SYMBOL] = value; element[LOADING_ATTR_SYMBOL] = value;
} }
if (value === null) { if (value == null) {
element.removeAttribute(attribute); element.removeAttribute(attribute);
} else {
if (attribute in element && typeof value !== 'string') {
// @ts-ignore
element[attribute] = value;
} else { } else {
element.setAttribute(attribute, value); element.setAttribute(attribute, value);
} }
} }
}
/** /**
* @param {Element} dom * @param {Element} dom
@ -287,18 +290,18 @@ export function set_attributes(
name = normalize_attribute(name); name = normalize_attribute(name);
} }
if (setters.includes(name)) { if (setters.includes(name) && typeof value !== 'string') {
if (hydrating && (name === 'src' || name === 'href' || name === 'srcset')) {
if (!skip_warning) check_src_in_dev_hydration(element, name, value);
} else {
// @ts-ignore // @ts-ignore
element[name] = value; element[name] = value;
}
} else if (typeof value !== 'function') { } else if (typeof value !== 'function') {
if (hydrating && (name === 'src' || name === 'href' || name === 'srcset')) {
if (!skip_warning) check_src_in_dev_hydration(element, name, value ?? '');
} else {
set_attribute(element, name, value); set_attribute(element, name, value);
} }
} }
} }
}
// On the first run, ensure that events are added after bindings so // On the first run, ensure that events are added after bindings so
// that their listeners fire after the binding listeners // that their listeners fire after the binding listeners
@ -389,12 +392,12 @@ function get_setters(element) {
/** /**
* @param {any} element * @param {any} element
* @param {string} attribute * @param {string} attribute
* @param {string | null} value * @param {string} value
*/ */
function check_src_in_dev_hydration(element, attribute, value) { function check_src_in_dev_hydration(element, attribute, value) {
if (!DEV) return; if (!DEV) return;
if (attribute === 'srcset' && srcset_url_equal(element, value)) return; if (attribute === 'srcset' && srcset_url_equal(element, value)) return;
if (src_url_equal(element.getAttribute(attribute) ?? '', value ?? '')) return; if (src_url_equal(element.getAttribute(attribute) ?? '', value)) return;
w.hydration_attribute_changed( w.hydration_attribute_changed(
attribute, attribute,
@ -420,12 +423,12 @@ function split_srcset(srcset) {
/** /**
* @param {HTMLSourceElement | HTMLImageElement} element * @param {HTMLSourceElement | HTMLImageElement} element
* @param {string | undefined | null} srcset * @param {string} srcset
* @returns {boolean} * @returns {boolean}
*/ */
function srcset_url_equal(element, srcset) { function srcset_url_equal(element, srcset) {
var element_urls = split_srcset(element.srcset); var element_urls = split_srcset(element.srcset);
var urls = split_srcset(srcset ?? ''); var urls = split_srcset(srcset);
return ( return (
urls.length === element_urls.length && urls.length === element_urls.length &&

@ -0,0 +1,21 @@
import { test } from '../../test';
export default test({
test({ assert, target }) {
const should_be_false = /** @type {HTMLDivElement[]} */ (
target.querySelectorAll('.translate-false div')
);
const should_be_true = /** @type {HTMLDivElement[]} */ (
target.querySelectorAll('.translate-true div')
);
should_be_false.forEach((div, i) => {
assert.equal(div.translate, false, `${i + 1} of ${should_be_false.length}: ${div.outerHTML}`);
});
should_be_true.forEach((div, i) => {
assert.equal(div.translate, true, `${i + 1} of ${should_be_true.length}: ${div.outerHTML}`);
});
}
});

@ -0,0 +1,19 @@
<div class="translate-false">
<div translate={false}></div>
<div translate="no"></div>
<div {...{ translate: false }}></div>
<div {...{ translate: 'no' }}></div>
</div>
<div class="translate-true">
<div></div>
<div translate={true}></div>
<div translate="yes"></div>
<div {...{ translate: true }}></div>
<div {...{ translate: 'yes' }}></div>
<div translate="false"></div>
<div translate="banana"></div>
<div {...{ translate: 'false' }}></div>
<div {...{ translate: 'banana' }}></div>
</div>
Loading…
Cancel
Save