@samitouri / QOS-React-2 / commits / 5476f9168b

Repair spacebar textInput

Browsers that natively support the `textInput` event appear to have a bug: when preventing default behavior for a `textInput` event occurring for a spacebar keypress, the character is prevented from being inserted **but the browser scrolls down**. Minimal repro example: http://jsfiddle.net/salier/bX4fw/ This is ridiculous, since scrolling makes no sense when the user is focused in a textinput or contenteditable. Preventing default at the `textInput` stage should mean to prevent the character from being inserted, and should have no impact at all on scrolling behavior. I have filed this as a Chromium bug (https://code.google.com/p/chromium/issues/detail?id=355103) but I'm not going to hold out much hope that they'll fix it. To resolve this, I'm special-casing the spacebar character at the plugin level, in `BeforeInputEventPlugin`. I looked for ways to do this at the component level, but it seems to me that this is simply a browser bug and it's cleaner to handle it there. In browsers that can use the native `textInput` event, I'm checking the code of the pressed key. If it's the spacebar, we dispatch the synthetic event as if there were no native `textInput` event -- as if we were running Firefox. Then, if the synthetic event is not canceled and we make it through to the native `textInput` event, bail if the character data is a space character.

Isaac Salier-Hellendag committed Mar 25, 2014 at 12:32 UTC 5476f9168beadda74506d37fd779e7e4bca0bc1c
1 file changed +45 -7
src/browser/eventPlugins/BeforeInputEventPlugin.js
+45 -7
@@ -26,12 +26,15 @@ var SyntheticInputEvent = require('SyntheticInputEvent');
26
27 var keyOf = require('keyOf');
28
29 -var useBeforeInputEvent = (
29 +var canUseTextInputEvent = (
30 ExecutionEnvironment.canUseDOM &&
31 'TextEvent' in window &&
32 !('documentMode' in document)
33 );
34
35 +var SPACEBAR_CODE = 32;
36 +var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
37 +
38 var topLevelTypes = EventConstants.topLevelTypes;
39
40 // Events and their corresponding property names.
@@ -100,13 +103,48 @@ var BeforeInputEventPlugin = {
103
104 var chars;
105
103 - if (useBeforeInputEvent) {
104 - // For browsers that support `textInput` events natively, don't do
105 - // anything with keypress, composition, etc.
106 - if (topLevelType !== topLevelTypes.topTextInput) {
107 - return;
106 + if (canUseTextInputEvent) {
107 + switch (topLevelType) {
108 + case topLevelTypes.topKeyPress:
109 + /**
110 + * If native `textInput` events are available, our goal is to make
111 + * use of them. However, there is a special case: the spacebar key.
112 + * In Webkit, preventing default on a spacebar `textInput` event
113 + * cancels character insertion, but it *also* causes the browser
114 + * to fall back to its default spacebar behavior of scrolling the
115 + * page.
116 + *
117 + * Tracking at:
118 + * https://code.google.com/p/chromium/issues/detail?id=355103
119 + *
120 + * To avoid this issue, use the keypress event as if no `textInput`
121 + * event is available.
122 + */
123 + var which = nativeEvent.which;
124 + if (which !== SPACEBAR_CODE) {
125 + return;
126 + }
127 +
128 + chars = String.fromCharCode(which);
129 + break;
130 +
131 + case topLevelTypes.topTextInput:
132 + // Record the characters to be added to the DOM.
133 + chars = nativeEvent.data;
134 +
135 + // If it's a spacebar character, assume that we have already handled
136 + // it at the keypress level and bail immediately.
137 + if (chars === SPACEBAR_CHAR) {
138 + return;
139 + }
140 +
141 + // Otherwise, carry on.
142 + break;
143 +
144 + default:
145 + // For other native event types, do nothing.
146 + return;
147 }
109 - chars = nativeEvent.data;
148 } else {
149 switch (topLevelType) {
150 case topLevelTypes.topPaste: