-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spread properties on inputs have unexpected readonly attributes #3764
Comments
If any of the attributes on an element are spread attributes, we're currently bailing on all of the logic we have for which attributes are boolean and should be set via properties. We need to continue using properties where appropriate. I'm guessing the |
Took an initial shot at this in this branch. This doesn't break any tests, and seems to generate more correct code in situations where we have property-only attributes (i.e., those with |
I suppose the most correct thing we could do (without shipping attribute lookup metadata in the compiled components) would be to split up the list of attributes and spreads into contiguous chunks of spreads-and-regular-attributes vs chunks of attributes-with-property-lookup-metada. We'd then produce one I suppose even better would be to render regular individual |
At first glance that looks sensible, but As an alternative, could we reconsider adding the tag properties mapping metadata to runtime? Since we're using Say: interface PropertyMap {
[attr: string]: string;
}
export function set_attributes(node: HTMLElement, attributes: { [x: string]: any }, properties_map: PropertyMap) {
for (const key in attributes) {
const value = attributes[key];
// this could be expanded to cover other special-cased
// attributes in AttributeWrapper, like selects
if (value == null) {
node.removeAttribute(key);
} else if (key === 'style') {
node.style.cssText = value;
} else if (key === 'hidden') {
node.hidden = value;
} else if (properties_map[key]) {
node[properties_map[key]] = value;
} else {
node.setAttribute(key, value);
}
}
} Where export const attrs_iframe = { allowfullscreen: 'allowFullscreen', allowpaymentrequest: 'allowPaymentRequest' };
export const attrs_script = { async: 'async', defer: 'defer', nomodule: 'noModule' };
export const attrs_button = { autofocus: 'autofocus', disabled: 'disabled', formnovalidate: 'formNoValidate', value: 'value' };
export const attrs_input = { autofocus: 'autofocus', checked: 'checked', disabled: 'disabled', formnovalidate: 'formNoValidate', indeterminate: 'indeterminate', multiple: 'multiple', readonly: 'readOnly', required: 'required', value: 'value' };
export const attrs_keygen = { autofocus: 'autofocus', disabled: 'disabled' };
export const attrs_select = { autofocus: 'autofocus', disabled: 'disabled', multiple: 'multiple', required: 'required', value: 'value' };
export const attrs_textarea = { autofocus: 'autofocus', disabled: 'disabled', readonly: 'readOnly', required: 'required', value: 'value' };
export const attrs_audio = { autoplay: 'autoplay', controls: 'controls', loop: 'loop', muted: 'muted' };
export const attrs_video = { autoplay: 'autoplay', controls: 'controls', loop: 'loop', muted: 'muted', playsinline: 'playsInline' };
export const attrs_track = { default: 'default' };
export const attrs_fieldset = { disabled: 'disabled' };
export const attrs_optgroup = { disabled: 'disabled' };
export const attrs_option = { disabled: 'disabled', selected: 'selected', value: 'value' };
export const attrs_img = { ismap: 'isMap' };
export const attrs_bgsound = { loop: 'loop' };
export const attrs_form = { novalidate: 'noValidate' };
export const attrs_details = { open: 'open' };
export const attrs_dialog = { open: 'open' };
export const attrs_ol = { reversed: 'reversed' };
export const attrs_li = { value: 'value' };
export const attrs_meter = { value: 'value' };
export const attrs_progress = { value: 'value' };
export const attrs_param = { value: 'value' };
export const attrs_any = { hidden: 'hidden' }; Now, if #3750 goes somewhere, the mapping might become a big dispatch table to setters instead... Then it might be too big (or not, thanks to minification? idk). |
If you have something like This also means that something like So: New angle of attack. Don't fight the |
Pushed to my branch the changes I mentioned in the last comment. All existing tests still pass, and now the generated update code for the element in question in the original repro is: set_attributes(input0, get_spread_update(input0_levels, [
changed.props && ctx.props,
changed.disabled && ({ disabled }),
changed.readonly && ({ readOnly: readonly })
])); i.e., we're now setting In light of #3750, runtime lookups might become impractical. I'd still like to try to avoid them if we can. What I have here seems to be a 'lightest touch' type of solution. |
If we go ahead with this type of solution, this actually won't fix #3680. We'll still need some way of setting |
* add failing tests * fix boolean attributes along with spreads (DOM mode) * fix boolean attributes along with spreads (SSR mode) * update changelog (#3764) * fix removing attributes in spreads
Describe the bug
Spread properties on
<input>
tags can have an unexpected behavior on readonly attributes. If a tag hasreadonly={false}
along with a spread, then the readonly attribute is included in the tag asreadonly="false"
. This results in a readonly input.To Reproduce
https://svelte.dev/repl/956c1eeb49b24d8c91942878eaedb4e1?version=3.12.1
Expected behavior
In the example provided, the readonly attribute should be omitted if false.
Information about your Svelte project:
Your browser and the version: Version 77.0.3865.120 (Official Build) (64-bit)
Your operating system: OS X 10
Svelte version 3.12.1
Severity
minor
The text was updated successfully, but these errors were encountered: