-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Questions: How should JSX attributes foo and foo={true} behave for an HTML element, as in <div foo> or <div foo={true}>? Should the behavior differ for components and/or spreads?
Current behavior in Solid:
Assume const Div = (props) => <div {...props}/>
| JSX | Equivalent HTML |
|---|---|
<div foo> |
<div foo=""> |
<div foo=""> |
<div foo=""> |
<div foo={true}> |
<div foo="true"> |
<div foo="true"> |
<div foo="true"> |
<Div foo> |
<div foo="true"> ☆ |
<Div foo=""> |
<div foo=""> |
<Div foo={true}> |
<div foo="true"> |
<Div foo="true"> |
<div foo="true"> |
The ☆ behavior (another consequence of setAttribute's string casting) is particularly counterintuitive, as it differs between HTML elements and component-wrapped elements. (It may also be an SSR discrepancy?) @fabiospampinato's observation of this weirdness is what spawned this exploration (in #templating channel, also with @lxsmnsyc).
Relevant facts:
- HTML 5 defines attribute
footo be equivalent tofoo="" - React defines that
foois equivalent tofoo={true}. - TypeScript treats
foolikefoo={true}(attribute should accept Boolean value) - React (specifically react-dom) converts JSX attribute
footo HTML equivalent offoo="true"unlessfoois on a whitelist of "Boolean" attributes, in which case it converts tofoo="". Here's a relevant list; note that there are some Boolean-like attributes which aren't actually Boolean. For example, React "correctly" convertsdraggable={true}todraggable="true"and "correctly" convertsasync={true}toasync="".
Desired properties:
foois always equivalent tofoo=""for HTML/SVG elements (as in HTML 5). This is useful for copy/pastability of HTML, which the JSX spec considers important.foois equivalent tofoo={true}(as in React and TypeScript)draggable={true}is equivalent todraggable="true"<div foo>is equivalent toconst Div = (props) => <div {...props}/>; <Div foo>
We can't have all four properties.
- Properties 1, 2, and 3 contradict each other: 1+2 imply
draggable={true}is equivalent todraggable="". - React satisfies Properties 2, 3, and 4. It follows Property 1 only for a whitelist of Boolean attributes.
- Solid satisfies Properties 1 and 2. It satisfies Property 3 for
Divbut notdiv. It doesn't satisfy Property 4 because of ☆.
Proposal:
I really like Property 1, and would propose gaining Property 4 by compiling foo={value} to essentially _el$.setAttribute('foo', value === true ? '' : value), where value is stored in a temporary variable if it's a general expression, with a similar modification to spreads. This would basically flip ☆, and also change those marked "!":
| JSX | Equivalent HTML |
|---|---|
<div foo> |
<div foo=""> |
<div foo=""> |
<div foo=""> |
<div foo={true}> |
<div foo=""> ! |
<div foo="true"> |
<div foo="true"> |
<Div foo> |
<div foo=""> ☆ |
<Div foo=""> |
<div foo=""> |
<Div foo={true}> |
<div foo=""> ! |
<Div foo="true"> |
<div foo="true"> |
(By contrast, React's approach of an attribute whitelist feels gross...)