-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Normalize onChange behavior in IE for inputs with placeholders #3826
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ var ReactClass = require('ReactClass'); | |
var ReactElement = require('ReactElement'); | ||
var ReactMount = require('ReactMount'); | ||
var ReactUpdates = require('ReactUpdates'); | ||
var ExecutionEnvironment = require('ExecutionEnvironment'); | ||
var isTextInputElement = require('isTextInputElement'); | ||
|
||
var assign = require('Object.assign'); | ||
var findDOMNode = require('findDOMNode'); | ||
|
@@ -27,6 +29,11 @@ var invariant = require('invariant'); | |
var input = ReactElement.createFactory('input'); | ||
|
||
var instancesByReactID = {}; | ||
var hasNoisyInputEvent = false; | ||
|
||
if (ExecutionEnvironment.canUseDOM) { | ||
hasNoisyInputEvent = 'documentMode' in document && document.documentMode > 9; | ||
} | ||
|
||
function forceUpdateIfMounted() { | ||
/*jshint validthis:true */ | ||
|
@@ -35,6 +42,15 @@ function forceUpdateIfMounted() { | |
} | ||
} | ||
|
||
|
||
function isIEInputEvent(event) { | ||
return ( | ||
hasNoisyInputEvent && | ||
event.nativeEvent.type === 'input' && | ||
isTextInputElement(event.target) | ||
); | ||
} | ||
|
||
/** | ||
* Implements an <input> native component that allows setting these optional | ||
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`. | ||
|
@@ -57,8 +73,20 @@ var ReactDOMInput = ReactClass.createClass({ | |
|
||
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin], | ||
|
||
componentWillMount: function() { | ||
var value = LinkedValueUtils.getValue(this.props); | ||
var defaultValue = this.props.defaultValue; | ||
|
||
if (defaultValue == null) { | ||
defaultValue = ''; | ||
} | ||
|
||
this._lastValue = value != null ? value : defaultValue; | ||
}, | ||
|
||
getInitialState: function() { | ||
var defaultValue = this.props.defaultValue; | ||
|
||
return { | ||
initialChecked: this.props.defaultChecked || false, | ||
initialValue: defaultValue != null ? defaultValue : null | ||
|
@@ -115,6 +143,22 @@ var ReactDOMInput = ReactClass.createClass({ | |
_handleChange: function(event) { | ||
var returnValue; | ||
var onChange = LinkedValueUtils.getOnChange(this.props); | ||
|
||
// IE 10+ fire input events when setting/unsetting a placeholder | ||
// we guard against it by checking if the next and | ||
// last values are both empty and bailing out of the change | ||
// https://github.com/facebook/react/issues/3484 | ||
if (isIEInputEvent(event)) { | ||
var lastValue = this._lastValue; | ||
|
||
this._lastValue = event.target.value; | ||
|
||
if ( event.target.value === '' && lastValue === '') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove space after opening paren |
||
event.stopPropagation(); | ||
return undefined; | ||
} | ||
} | ||
|
||
if (onChange) { | ||
returnValue = onChange.call(this, event); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin'); | |
var ReactClass = require('ReactClass'); | ||
var ReactElement = require('ReactElement'); | ||
var ReactUpdates = require('ReactUpdates'); | ||
var ExecutionEnvironment = require('ExecutionEnvironment'); | ||
|
||
var assign = require('Object.assign'); | ||
var findDOMNode = require('findDOMNode'); | ||
|
@@ -26,6 +27,11 @@ var invariant = require('invariant'); | |
var warning = require('warning'); | ||
|
||
var textarea = ReactElement.createFactory('textarea'); | ||
var hasNoisyInputEvent = false; | ||
|
||
if (ExecutionEnvironment.canUseDOM) { | ||
hasNoisyInputEvent = 'documentMode' in document && document.documentMode > 9; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpicking: couldn't you just use: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true, though the current way is consistent with similar checks elsewhere in the code base |
||
} | ||
|
||
function forceUpdateIfMounted() { | ||
/*jshint validthis:true */ | ||
|
@@ -34,6 +40,13 @@ function forceUpdateIfMounted() { | |
} | ||
} | ||
|
||
function isIEInputEvent(event) { | ||
return ( | ||
hasNoisyInputEvent && | ||
event.nativeEvent.type === 'input' | ||
); | ||
} | ||
|
||
/** | ||
* Implements a <textarea> native component that allows setting `value`, and | ||
* `defaultValue`. This differs from the traditional DOM API because value is | ||
|
@@ -55,6 +68,17 @@ var ReactDOMTextarea = ReactClass.createClass({ | |
|
||
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin], | ||
|
||
componentWillMount: function() { | ||
var value = LinkedValueUtils.getValue(this.props); | ||
var defaultValue = this.props.defaultValue; | ||
|
||
if (defaultValue == null) { | ||
defaultValue = ''; | ||
} | ||
|
||
this._lastValue = value != null ? value : defaultValue; | ||
}, | ||
|
||
getInitialState: function() { | ||
var defaultValue = this.props.defaultValue; | ||
// TODO (yungsters): Remove support for children content in <textarea>. | ||
|
@@ -125,6 +149,22 @@ var ReactDOMTextarea = ReactClass.createClass({ | |
_handleChange: function(event) { | ||
var returnValue; | ||
var onChange = LinkedValueUtils.getOnChange(this.props); | ||
|
||
// IE 10+ fire input events when setting/unsetting a placeholder | ||
// we guard against it by checking if the next and | ||
// last values are both empty and bailing out of the change | ||
// https://github.com/facebook/react/issues/3484 | ||
if (isIEInputEvent(event)) { | ||
var lastValue = this._lastValue; | ||
|
||
this._lastValue = event.target.value; | ||
|
||
if ( event.target.value === '' && lastValue === '') { | ||
event.stopPropagation(); | ||
return undefined; | ||
} | ||
} | ||
|
||
if (onChange) { | ||
returnValue = onChange.call(this, event); | ||
} | ||
|
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.
2 things here.
isIEInputEvent
&&
at the end of the previous lines and then let's wrap the whole return in parensAnd now actually a 3rd since I'm looking. We can cache the value of
'documentMode' in document && document.documentMode > 9
so that we don't need to look it up each time (any access of DOM properties has a cost).