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
foo to be equivalent to foo=""
- React defines that
foo is equivalent to foo={true}.
- TypeScript treats
foo like foo={true} (attribute should accept Boolean value)
- React (specifically react-dom) converts JSX attribute
foo to HTML equivalent of foo="true" unless foo is on a whitelist of "Boolean" attributes, in which case it converts to foo="". Here's a relevant list; note that there are some Boolean-like attributes which aren't actually Boolean. For example, React "correctly" converts draggable={true} to draggable="true" and "correctly" converts async={true} to async="".
Desired properties:
foo is always equivalent to foo="" for HTML/SVG elements (as in HTML 5). This is useful for copy/pastability of HTML, which the JSX spec considers important.
foo is equivalent to foo={true} (as in React and TypeScript)
draggable={true} is equivalent to draggable="true"
<div foo> is equivalent to const 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 to draggable="".
- 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
Div but not div. 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...)
Questions: How should JSX attributes
fooandfoo={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}/><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:
footo be equivalent tofoo=""foois equivalent tofoo={true}.foolikefoo={true}(attribute should accept Boolean value)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.
draggable={true}is equivalent todraggable="".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), wherevalueis 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 "!":<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...)