-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Fix IE9 Undo Bug #12505
Fix IE9 Undo Bug #12505
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 |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import Fixture from '../../Fixture'; | ||
const React = window.React; | ||
|
||
const noop = () => {}; | ||
|
||
class BubbledInputTestCase extends React.Component { | ||
state = { | ||
value: '', | ||
}; | ||
|
||
onChange = event => { | ||
this.setState({ | ||
value: (event.srcElement || event.target).value, | ||
}); | ||
}; | ||
|
||
render() { | ||
return ( | ||
<Fixture> | ||
<div className="control-box" onChange={this.onChange}> | ||
<fieldset> | ||
<legend>Controlled Text</legend> | ||
<input value={this.state.value} onChange={noop} /> | ||
<p className="hint">Value: {JSON.stringify(this.state.value)}</p> | ||
</fieldset> | ||
</div> | ||
</Fixture> | ||
); | ||
} | ||
} | ||
|
||
export default BubbledInputTestCase; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ import Fixture from '../../Fixture'; | |
import FixtureSet from '../../FixtureSet'; | ||
import TestCase from '../../TestCase'; | ||
import InputTestCase from './InputTestCase'; | ||
import BubbledInputTestCase from './BubbledInputTestCase'; | ||
import ReplaceEmailInput from './ReplaceEmailInput'; | ||
|
||
const React = window.React; | ||
|
@@ -141,6 +142,22 @@ class TextInputFixtures extends React.Component { | |
<ReplaceEmailInput /> | ||
</TestCase> | ||
|
||
<TestCase | ||
title="Bubbled Change Events" | ||
affectedBrowsers="IE9" | ||
relatedIssues="708,11609"> | ||
<TestCase.Steps> | ||
<li>Enter text</li> | ||
</TestCase.Steps> | ||
|
||
<TestCase.ExpectedResult> | ||
Each input's value should change as expected, despite the change | ||
event being attached to the parent element | ||
</TestCase.ExpectedResult> | ||
|
||
<BubbledInputTestCase type="text" /> | ||
</TestCase> | ||
|
||
<TestCase title="All inputs" description="General test of all inputs"> | ||
<InputTestCase type="text" defaultValue="Text" /> | ||
<InputTestCase type="email" defaultValue="[email protected]" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,8 @@ | |
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import * as EventPluginHub from 'events/EventPluginHub'; | ||
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators'; | ||
import {enqueueStateRestore} from 'events/ReactControlledComponent'; | ||
import {batchedUpdates} from 'events/ReactGenericBatching'; | ||
import SyntheticEvent from 'events/SyntheticEvent'; | ||
import isTextInputElement from 'shared/isTextInputElement'; | ||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'; | ||
|
@@ -23,7 +21,6 @@ import { | |
TOP_KEY_UP, | ||
TOP_SELECTION_CHANGE, | ||
} from './DOMTopLevelEventTypes'; | ||
import getEventTarget from './getEventTarget'; | ||
import isEventSupported from './isEventSupported'; | ||
import {getNodeFromInstance} from '../client/ReactDOMComponentTree'; | ||
import * as inputValueTracking from '../client/inputValueTracking'; | ||
|
@@ -77,31 +74,6 @@ function shouldUseChangeEvent(elem) { | |
); | ||
} | ||
|
||
function manualDispatchChangeEvent(nativeEvent) { | ||
const event = createAndAccumulateChangeEvent( | ||
activeElementInst, | ||
nativeEvent, | ||
getEventTarget(nativeEvent), | ||
); | ||
|
||
// If change and propertychange bubbled, we'd just bind to it like all the | ||
// other events and have it go through ReactBrowserEventEmitter. Since it | ||
// doesn't, we manually listen for the events and so we have to enqueue and | ||
// process the abstract event manually. | ||
// | ||
// Batching is necessary here in order to ensure that all event handlers run | ||
// before the next rerender (including event handlers attached to ancestor | ||
// elements instead of directly on the input). Without this, controlled | ||
// components don't work properly in conjunction with event bubbling because | ||
// the component is rerendered and the value reverted before all the event | ||
// handlers can run. See https://github.com/facebook/react/issues/708. | ||
batchedUpdates(runEventInBatch, event); | ||
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. This code dispatches a change event; it doesn't do anything by itself. Can we figure out why this was breaking? 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. I don't really feel comfortable removing this logic until we understand why it's buggy. 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. Totally, 👍 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. As far as I can tell, this code executes correctly. I believe the issue is a difference in browser behavior when using Try this in IE9: Browse the code: It looks like IE9, when using I think we were able to get away with this before (but I'm not super familiar) because we identified text via an embedded span, and then eventually relative to a comment. 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. This is... probably the strangest DOM thing I have seen. I would love a second/third opinion here. 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. wouldn't the onInput event catch that as well? Or does it not fire in that case for ie9? 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. According to MDN:
https://developer.mozilla.org/en-US/docs/Web/Events/input (footnote 2) 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. right, deleting doesn't work, we have the 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. Right, right I thought you were asking about 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. I wonder if we could catch some of these edge cases by comparing the value on focus: I'm not sure what to do about "Cut", though I can't help but wonder if it triggers a selection change (you have text selected, you cut, and thus, the words disappear and selection changes). |
||
} | ||
|
||
function runEventInBatch(event) { | ||
EventPluginHub.runEventsInBatch(event, false); | ||
} | ||
|
||
function getInstIfValueChanged(targetInst) { | ||
const targetNode = getNodeFromInstance(targetInst); | ||
if (inputValueTracking.updateValueIfChanged(targetNode)) { | ||
|
@@ -135,7 +107,6 @@ if (ExecutionEnvironment.canUseDOM) { | |
function startWatchingForValueChange(target, targetInst) { | ||
activeElement = target; | ||
activeElementInst = targetInst; | ||
activeElement.attachEvent('onpropertychange', handlePropertyChange); | ||
} | ||
|
||
/** | ||
|
@@ -146,24 +117,10 @@ function stopWatchingForValueChange() { | |
if (!activeElement) { | ||
return; | ||
} | ||
activeElement.detachEvent('onpropertychange', handlePropertyChange); | ||
activeElement = null; | ||
activeElementInst = null; | ||
} | ||
|
||
/** | ||
* (For IE <=9) Handles a propertychange event, sending a `change` event if | ||
* the value of the active element has changed. | ||
*/ | ||
function handlePropertyChange(nativeEvent) { | ||
if (nativeEvent.propertyName !== 'value') { | ||
return; | ||
} | ||
if (getInstIfValueChanged(activeElementInst)) { | ||
manualDispatchChangeEvent(nativeEvent); | ||
} | ||
} | ||
|
||
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. @jquense As far as I can test, this behavior is no longer necessary. Do you know what conditions 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. @sophiebits original blog post on this is my primary text on the subject :P https://sophiebits.com/2013/06/18/a-near-perfect-oninput-shim-for-ie-8-and-9.html I think you are right, I was reticent to yank it all out out of an abundance of caution, but it should be ok to remove (as well as the keyup and down handlers with selectionchange below) |
||
function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) { | ||
if (topLevelType === TOP_FOCUS) { | ||
// In IE9, propertychange fires for most input events but is buggy and | ||
|
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.
We need this until #12976 lands