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
Indistinguishable properties and attributes #56
Comments
@mindplay-dk I don't use What about Preact/React, I don't recall they having any special syntax for distinguishing attributes and props and I think that's great, because it makes it easier to reason about my user interface. As a user, I don't really want to think about attributes and props as separate entities. This was a particular pain point with Snabbdom, when I used it, before Hyperapp/Picodom. |
Yes, but I think this is one of the reasons why these frameworks are considerably larger and more complex - they make distinctions between different element-types and their attributes. For example, React use code like this to detect specific element types: They have to make distinctions and run-time, and that requires knowledge of every element type, it's properties and attributes - in fact, they make numerous distinctions beyond just properties and attributes, here:
It's not unthinkable that we can find a much simpler way to deal with this, but it most likely will need to include either a blacklist or whitelist of some sort? |
@mindplay-dk I don't think Preact could afford that. |
@mindplay-dk You already gave one good example, What are other props/attributes examples that you think should be treated differently? |
There are interesting edge-cases - for example, if you do Custom properties don't map to attributes:
But many built-in properties do map to an attribute:
So, some properties update their attribute counterpart as a side-effect, and vice-versa. Fun times. I wonder if we can exploit this somehow? |
Sure - it can do everything at compile-time. |
Here's the relevant portion of the Preact sources for reference: if (name!=='list' && name!=='type' && !isSvg && name in node) {
setProperty(node, name, value==null ? '' : value);
if (value==null || value===false) node.removeAttribute(name);
}
else {
let ns = isSvg && (name !== (name = name.replace(/^xlink\:?/, '')));
if (value==null || value===false) {
if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase());
else node.removeAttribute(name);
}
else if (typeof value!=='function') {
if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value);
else node.setAttribute(name, value);
}
} It's doing something like what I suggested - it's checking for I'm experimenting with something similar, I'll let you know what I find... |
…g further discussion in jorgebucaran#56)
I'm close to a solution, but the test-framework seems to contain an inconsistency with real browsers. In a real brower, setting the In seems, in the test framework, that's not the case. In fact, there's a passing test in the current test-suite, which I think should actually fail: testTrees("update element with dynamic props", [
{
node: h("input", {
type: "text",
oncreate(element) {
element.value = "bar"
},
value: "foo"
}),
html: `<input type="text" value="foo">`
}, Here, the test says that the initialization via props testTrees("update element with dynamic props", [
{
node: h("input", {
type: "text",
oncreate(element) {
//element.value = "bar"
element.setAttribute("value", "bar")
},
value: "foo"
}),
html: `<input type="text" value="foo">`
},
But setting the Try setting I'm reluctant to change the test before checking this with you, but I think the test is wrong, and it's passing because of a bug in the test-framework? So I have two failing tests as of right now:
The first is what I just explained. The second one, I think, is actually correctly omitting the |
@mindplay-dk Please don't worry so much about tests for now. If you have a working PR addressing the issues you were having, I'd like to see it first. That way I can understand what this issue is really about, because I have to confess that I am still confused. Or perhaps I am just not that concerned about 100% accuracy, as long as "it works". Show me the code! 💸 😄 |
It's on this branch :-) |
@mindplay-dk Do you think it would be possible to refactor this part: if (null == value || false === value) {
element.removeAttribute(name)
} ...so that it is not repeated, but reused for both branches. |
Yes, though this requires the introduction of a var |
What do you think about the failing tests? Do you agree that this might be a bug in the test-framework? From what I could find, this is not how a real browser behaves - but I honestly have very little experience with testing in JS, and I'm not even sure which package or component is at fault. |
@mindplay-dk Sorry, I was not aware of any failing tests. If you can send me a PR with these changes so we can review it, please do. |
There are two failing tests, as explained above - the first seems like it should have failed all along (before I changed anything) but may be passing due to a bug in the test-framework. I'm really not confident enough to say that with certainty. And as explained, because of the same bug, I can't make the other test pass - although I'm fairly certain it would pass in a real browser. As said, I don't know enough about this test-framework to even say which package is at fault. So I'm kind of stuck with both of these :-/ |
@mindplay-dk I have a question: are you treating values as properties only if the key is |
I'm treating values as properties if they map to properties ;-) let foo = { bar: "baz" }
let name = "bar"
name in foo // true |
@mindplay-dk I was reading it wrong. Thanks! This looks good, could you make a PR? |
Yeah, but... I have two failing tests and no idea how to fix either of them. |
Well, in that case I guess I'll help myself with your fork and try to come up with a solution myself! 💪😄 |
An update on this: the test-framework isn't wrong, the tests are! The assertions for these tests are wrong:
That is, we are getting the expected For certain other attributes such as I'll see if I can correct the tests. |
Bingo, the tests are passing 😄 I've opened a PR which I think is ready for review now. |
Currently, the
patch()
function treats every JSX attribute simultaneously as a property and an attribute - it initializes both, which means you have no ability to update them individually.Blindly treating every JSX attribute as both a property and an attribute means that most updates are unnecessarily being done twice - one of them generating either a meaningless property or a meaningless DOM attribute. The DOM accumulates a lot of garbage this way.
For example,
<div innerHTML={content}>
will inject literal HTML via theinnerHTML
property, but will also create a uselessinnerHTML
attribute - or in some cases, such as<input value={value}>
, will initialize the value twice, first using thevalue
property, then again usingsetAttribute('value', ...)
.I don't have a lot of references, but
snabbdom
for one separates properties from attributes - which seems sort of inevitable, at the VDOM level, if we want to solve this problem?The text was updated successfully, but these errors were encountered: