diff --git a/src/browser/ui/dom/components/ReactDOMInput.js b/src/browser/ui/dom/components/ReactDOMInput.js index 49f783c1e1264..b1910d1186295 100644 --- a/src/browser/ui/dom/components/ReactDOMInput.js +++ b/src/browser/ui/dom/components/ReactDOMInput.js @@ -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 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 === '') { + event.stopPropagation(); + return undefined; + } + } + if (onChange) { returnValue = onChange.call(this, event); } diff --git a/src/browser/ui/dom/components/ReactDOMTextarea.js b/src/browser/ui/dom/components/ReactDOMTextarea.js index 0261005c40ca4..666a1e1716668 100644 --- a/src/browser/ui/dom/components/ReactDOMTextarea.js +++ b/src/browser/ui/dom/components/ReactDOMTextarea.js @@ -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; +} function forceUpdateIfMounted() { /*jshint validthis:true */ @@ -34,6 +40,13 @@ function forceUpdateIfMounted() { } } +function isIEInputEvent(event) { + return ( + hasNoisyInputEvent && + event.nativeEvent.type === 'input' + ); +} + /** * Implements a