-
Notifications
You must be signed in to change notification settings - Fork 50k
Fix the "checked" attribute is not initially set on the input #13114
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
Conversation
nhunzaker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sending this in!
I want to see if we can simplify the DOM operations by directly assigning our pre-computed initial checked value to node.defaultValue. Is this something you could look into?
| // Set the "checked" attribute initially. | ||
| if (props.hasOwnProperty('defaultChecked') && !!props.defaultChecked) { | ||
| node.setAttribute('checked', 'checked'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if lines 245-250 could be replaced with:
node.defaultChecked = node._wrapperState.initialCheckedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. The node._wrapperState.initialChecked will simplify the DOM operations.
136bea6 to
3e8ca2f
Compare
| } | ||
| node.defaultChecked = !node.defaultChecked; | ||
| node.defaultChecked = !node._wrapperState.initialChecked; | ||
| node.defaultChecked = !node.defaultChecked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about reversing these lines? I have a hard time tracking lots of boolean flipping. Do you think this would work?
node.defaultChecked = !node.defaultChecked;
node.defaultChecked = node._wrapperState.initialChecked;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remind me again why we set defaultChecked twice, is it related to value detachment? It'd be great if we could add a comment here explaining the reasoning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aweary Actually I don't know either, but I aired on the side of avoiding speculation, haha. I can do some testing to see if we really need this.
A lot of the value detachment stuff was added when defaultValue was assigned as an attribute through DOMPropertyOperations. I think those edge cases went away when we moved everything into ReactDOMInput.
The thing to figure out is if assignment detaches, or if the value truly must be different. It would be easy to run a few tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we set defaultChecked twice for solving Checkbox checked state is erroneously influenced by value of defaultChecked in Chrome.
test:
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test (function() {
+ var el = document.createElement("input");
+ el.setAttribute("type", "checkbox");
+ el.defaultChecked = true;
+ el.checked = true;
+ el.defaultChecked = false;
+ assert_true(el.checked);
+}, "This test check that checked state of checkbox once explicitly set is not affected by default state.");
+</script>
+</body>
+</html>
diff:
void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior)
{
+ m_reflectsCheckedAttribute = false;
if (checked() == nowChecked)
return;
- m_reflectsCheckedAttribute = false;
m_isChecked = nowChecked;
if (RadioButtonGroupScope* scope = radioButtonGroupScope())
The purpose of the first assignment is set the m_reflectsCheckedAttribute to false. So, both of our codes work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good enough answer for me, 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still, just to keep the boolean flipping in check, is it possible to do this:
node.defaultChecked = !node.defaultChecked;
node.defaultChecked = node._wrapperState.initialChecked;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nhunzaker updated.
And i changed test case because of which i found:
if a <input /> is neither checkbox nor radio, input.defaultChecked = true will call input.setAttribute('checked', '') but input.getAttribute('checked') still be null. Thereafter, input.defaultChecked = false will not call input.setAttribute on the uncheckable input.
b56f29d to
146bb96
Compare
nhunzaker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. I just left a final change requesting the use of hasAttribute over comparing getAttribute.
Thanks for sending this in!
| expect(aNode.getAttribute('checked')).toBe(''); | ||
| expect(bNode.getAttribute('checked')).toBe(null); | ||
| expect(cNode.getAttribute('checked')).toBe(''); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think one last change. This looks good overall.
Can you change this test to use hasAttribute instead of doing a value check on getAttribute? Like:
expect(aNode.hasAttribute('checked')).toBe(true);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, Updated.
| } | ||
| node.defaultChecked = !node.defaultChecked; | ||
| node.defaultChecked = !node.defaultChecked; | ||
| node.defaultChecked = !!node._wrapperState.initialChecked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just trying to think through all of the cases here:
checked or defaultChecked are true
node.defaultChecked = true // node.defaultChecked = !node.defaultChecked
node.defaultChecked = true // node.defaultChecked = !!falsechecked or defaultChecked are false
node.defaultChecked = true // node.defaultChecked = !node.defaultChecked
node.defaultChecked = false // node.defaultChecked = !!falsechecked and defaultChecked are undefined
This can happen if you just declare a regular text input, ex: <input value="test" />
In this case, value detachment still needs to occur. An input might switch from text to radio later, and React doesn't fire the post mount hook behavior when that happens.
node.defaultChecked = true // node.defaultChecked = !node.defaultChecked
node.defaultChecked = false // node.defaultChecked = !!undefinedSo I think this is good to go. Are there any other cases you can think of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separately, part of me wonders if node._wrapperState.initialChecked should always be a boolean. Like we should do the !!node._wrapperState.initialChecked part when initialChecked is assigned.
However I don't think that's related to this issue, and I'm not sure what existing code relies on that value to be loosely null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, i tried and got a Flow type check error.
Cannot assign node._wrapperState.initialChecked to node.defaultChecked because property defaultChecked is missing in object type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. One thing at a time. I think this is a great follow-up, but I'd love to get this check attribute fix in first.
| 'node.value = "1980-01-01"', | ||
| 'node.setAttribute("value", "1980-01-01")', | ||
| 'node.setAttribute("checked", "")', | ||
| 'node.setAttribute("checked", "")', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just so I can better understand this change, is the reduction of this setAttribute call due to the following case?
checked or defaultChecked are true
// <input defaultChecked=true} />
node.defaultChecked = true // node.defaultChecked = !node.defaultChecked
node.defaultChecked = true // node.defaultChecked = !!trueThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. Didn't catch your comment earlier. This makes sense!
…et on the input
146bb96 to
a3b6d44
Compare
nhunzaker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great from my end. I'll do some manual QA just to feel things out across a few browsers, but this is good as far as I'm concerned.
@aweary any thoughts?
|
Did some testing. This looks great. Thanks @dilidili! |
Fix issue #12765