Skip to content

Commit eb7e32c

Browse files
authored
fix: collect all necessary setters of html elements (#11371)
When spreading attributes, the setters of the element are checked. If they contain the key in question, it's set via that setter. For certain setters on certain elements this didn't work because the element prototype was not HTMLElement, rather a descendant of that (for example HTMLDivElement), which meant that only the setters of the descendant, not the superclass were taken into account. This fixes that by walking up the prototype chain until we find the Element prototype. fixes #11179
1 parent cd25065 commit eb7e32c

File tree

5 files changed

+46
-10
lines changed

5 files changed

+46
-10
lines changed

.changeset/blue-lemons-wait.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: collect all necessary setters of html elements when spreading attributes

packages/svelte/src/internal/client/dom/elements/attributes.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DEV } from 'esm-env';
22
import { hydrating } from '../hydration.js';
3-
import { get_descriptors, map_get, map_set, object_assign } from '../../utils.js';
3+
import { get_descriptors, get_prototype_of, map_get, map_set } from '../../utils.js';
44
import { AttributeAliases, DelegatedEvents, namespace_svg } from '../../../../constants.js';
55
import { delegate } from './events.js';
66
import { autofocus } from './misc.js';
@@ -241,14 +241,20 @@ var setters_cache = new Map();
241241
function get_setters(element) {
242242
/** @type {string[]} */
243243
var setters = [];
244+
var descriptors;
245+
var proto = get_prototype_of(element);
244246

245-
// @ts-expect-error
246-
var descriptors = get_descriptors(element.__proto__);
247+
// Stop at Element, from there on there's only unnecessary setters we're not interested in
248+
while (proto.constructor.name !== 'Element') {
249+
descriptors = get_descriptors(proto);
247250

248-
for (var key in descriptors) {
249-
if (descriptors[key].set && !always_set_through_set_attribute.includes(key)) {
250-
setters.push(key);
251+
for (var key in descriptors) {
252+
if (descriptors[key].set && !always_set_through_set_attribute.includes(key)) {
253+
setters.push(key);
254+
}
251255
}
256+
257+
proto = get_prototype_of(proto);
252258
}
253259

254260
return setters;

packages/svelte/tests/runtime-browser/test-ssr.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ export async function run_ssr_test(
1717
test_dir: string
1818
) {
1919
try {
20-
await compile_directory(test_dir, 'server', {
21-
...config.compileOptions,
22-
runes: test_dir.includes('runtime-runes')
23-
});
20+
await compile_directory(test_dir, 'server', config.compileOptions);
2421

2522
const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default;
2623
const { html } = render(Component, { props: config.props || {} });
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
async test({ target, assert }) {
5+
const div = target.querySelector('div');
6+
const btn = target.querySelector('button');
7+
8+
assert.equal(div?.hidden, true);
9+
10+
await btn?.click();
11+
assert.equal(div?.hidden, false);
12+
}
13+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
let hidden = $state(true);
3+
4+
const restProps = {
5+
id: '123'
6+
}
7+
</script>
8+
9+
<button onclick={() => hidden = !hidden}>
10+
toggle hidden
11+
</button>
12+
13+
<div {...restProps} hidden={hidden}>
14+
hello world (with spread attrs)
15+
</div>

0 commit comments

Comments
 (0)