Skip to content

Commit b44f949

Browse files
committed
implement for defaultValue/defaultChecked on inputs
1 parent 6d0b324 commit b44f949

File tree

6 files changed

+67
-34
lines changed

6 files changed

+67
-34
lines changed

packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @import { AST } from '#compiler' */
22
/** @import { Context } from '../types' */
3-
import { is_mathml, is_svg, is_void } from '../../../../utils.js';
3+
import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js';
44
import {
55
is_tag_valid_with_ancestor,
66
is_tag_valid_with_parent
@@ -77,9 +77,7 @@ export function RegularElement(node, context) {
7777

7878
if (
7979
node.attributes.some(
80-
(attribute) =>
81-
attribute.type === 'Attribute' &&
82-
(attribute.name === 'autofocus' || attribute.name === 'muted')
80+
(attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name)
8381
)
8482
) {
8583
mark_subtree_dynamic(context.path);

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
55
/** @import { Scope } from '../../../scope' */
66
import {
7+
cannot_be_set_statically,
78
is_boolean_attribute,
89
is_dom_property,
910
is_load_error_element,
@@ -181,20 +182,28 @@ export function RegularElement(node, context) {
181182
}
182183
}
183184

184-
if (
185-
node.name === 'input' &&
186-
(has_spread ||
187-
bindings.has('value') ||
188-
bindings.has('checked') ||
189-
bindings.has('group') ||
190-
attributes.some(
191-
(attribute) =>
192-
attribute.type === 'Attribute' &&
193-
(attribute.name === 'value' || attribute.name === 'checked') &&
194-
!is_text_attribute(attribute)
195-
))
196-
) {
197-
context.state.init.push(b.stmt(b.call('$.remove_input_defaults', context.state.node)));
185+
if (node.name === 'input') {
186+
const has_value_attribute = attributes.some(
187+
(attribute) =>
188+
attribute.type === 'Attribute' &&
189+
(attribute.name === 'value' || attribute.name === 'checked') &&
190+
!is_text_attribute(attribute)
191+
);
192+
const has_default_value_attribute = attributes.some(
193+
(attribute) =>
194+
attribute.type === 'Attribute' &&
195+
(attribute.name === 'defaultValue' || attribute.name === 'defaultChecked')
196+
);
197+
if (
198+
!has_default_value_attribute &&
199+
(has_spread ||
200+
bindings.has('value') ||
201+
bindings.has('checked') ||
202+
bindings.has('group') ||
203+
(!bindings.has('group') && has_value_attribute))
204+
) {
205+
context.state.init.push(b.stmt(b.call('$.remove_input_defaults', context.state.node)));
206+
}
198207
}
199208

200209
if (node.name === 'textarea') {
@@ -272,8 +281,7 @@ export function RegularElement(node, context) {
272281

273282
if (
274283
!is_custom_element &&
275-
attribute.name !== 'autofocus' &&
276-
attribute.name !== 'muted' &&
284+
!cannot_be_set_statically(attribute.name) &&
277285
(attribute.value === true || is_text_attribute(attribute))
278286
) {
279287
const name = get_attribute_name(node, attribute);

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @import { Expression } from 'estree' */
22
/** @import { AST, SvelteNode } from '#compiler' */
33
/** @import { ComponentContext } from '../../types' */
4+
import { cannot_be_set_statically } from '../../../../../../utils.js';
45
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
56
import * as b from '../../../../../utils/builders.js';
67
import { is_inlinable_expression } from '../../utils.js';
@@ -141,7 +142,7 @@ function is_static_element(node, state) {
141142
return false;
142143
}
143144

144-
if (attribute.name === 'autofocus' || attribute.name === 'muted') {
145+
if (cannot_be_set_statically(attribute.name)) {
145146
return false;
146147
}
147148

packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export function build_element_attributes(node, context) {
8282
) {
8383
events_to_capture.add(attribute.name);
8484
}
85-
} else {
85+
// the defaultValue/defaultChecked properties don't exist as attributes
86+
} else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') {
8687
if (attribute.name === 'class') {
8788
class_index = attributes.length;
8889
} else if (attribute.name === 'style') {

packages/svelte/src/internal/client/dom/elements/bindings/input.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DEV } from 'esm-env';
22
import { render_effect, teardown } from '../../../reactivity/effects.js';
3-
import { listen_to_event_and_reset_event, without_reactive_context } from './shared.js';
3+
import { listen_to_event_and_reset_event } from './shared.js';
44
import * as e from '../../../errors.js';
55
import { is } from '../../../proxy.js';
66
import { queue_micro_task } from '../../task.js';
@@ -34,6 +34,17 @@ export function bind_value(input, get, set = get) {
3434
}
3535
});
3636

37+
if (
38+
// If we are hydrating and the value has since changed,
39+
// then use the update value from the input instead.
40+
(hydrating && input.defaultValue !== input.value) ||
41+
// If defaultValue is set, then value == defaultValue
42+
// TODO Svelte 6: remove input.value check and set to empty string?
43+
(get() == null && input.value)
44+
) {
45+
set(input.value);
46+
}
47+
3748
render_effect(() => {
3849
if (DEV && input.type === 'checkbox') {
3950
// TODO should this happen in prod too?
@@ -42,13 +53,6 @@ export function bind_value(input, get, set = get) {
4253

4354
var value = get();
4455

45-
// If we are hydrating and the value has since changed, then use the update value
46-
// from the input instead.
47-
if (hydrating && input.defaultValue !== input.value) {
48-
set(input.value);
49-
return;
50-
}
51-
5256
if (is_numberlike_input(input) && value === to_number(input.value)) {
5357
// handles 0 vs 00 case (see https://github.com/sveltejs/svelte/issues/9959)
5458
return;
@@ -180,8 +184,14 @@ export function bind_checked(input, get, set = get) {
180184
set(value);
181185
});
182186

183-
if (get() == undefined) {
184-
set(false);
187+
if (
188+
// If we are hydrating and the value has since changed,
189+
// then use the update value from the input instead.
190+
(hydrating && input.defaultChecked !== input.checked) ||
191+
// If defaultChecked is set, then checked == defaultChecked
192+
get() == null
193+
) {
194+
set(input.checked);
185195
}
186196

187197
render_effect(() => {

packages/svelte/src/utils.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ const ATTRIBUTE_ALIASES = {
192192
ismap: 'isMap',
193193
nomodule: 'noModule',
194194
playsinline: 'playsInline',
195-
readonly: 'readOnly'
195+
readonly: 'readOnly',
196+
defaultvalue: 'defaultValue',
197+
defaultchecked: 'defaultChecked'
196198
};
197199

198200
/**
@@ -212,7 +214,9 @@ const DOM_PROPERTIES = [
212214
'readOnly',
213215
'value',
214216
'inert',
215-
'volume'
217+
'volume',
218+
'defaultValue',
219+
'defaultChecked'
216220
];
217221

218222
/**
@@ -222,6 +226,17 @@ export function is_dom_property(name) {
222226
return DOM_PROPERTIES.includes(name);
223227
}
224228

229+
const NON_STATIC_PROPERTIES = ['autofocus', 'muted', 'defaultValue', 'defaultChecked'];
230+
231+
/**
232+
* Returns `true` if the given attribute cannot be set through the template
233+
* string, i.e. needs some kind of JavaScript handling to work.
234+
* @param {string} name
235+
*/
236+
export function cannot_be_set_statically(name) {
237+
return NON_STATIC_PROPERTIES.includes(name);
238+
}
239+
225240
/**
226241
* Subset of delegated events which should be passive by default.
227242
* These two are already passive via browser defaults on window, document and body.

0 commit comments

Comments
 (0)