From 5476f9168beadda74506d37fd779e7e4bca0bc1c Mon Sep 17 00:00:00 2001 From: Isaac Salier-Hellendag Date: Tue, 25 Mar 2014 12:32:42 -0700 Subject: [PATCH] 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. --- .../eventPlugins/BeforeInputEventPlugin.js | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/browser/eventPlugins/BeforeInputEventPlugin.js b/src/browser/eventPlugins/BeforeInputEventPlugin.js index 3031305f94..c7ef71b988 100644 --- a/src/browser/eventPlugins/BeforeInputEventPlugin.js +++ b/src/browser/eventPlugins/BeforeInputEventPlugin.js @@ -26,12 +26,15 @@ var SyntheticInputEvent = require('SyntheticInputEvent'); var keyOf = require('keyOf'); -var useBeforeInputEvent = ( +var canUseTextInputEvent = ( ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !('documentMode' in document) ); +var SPACEBAR_CODE = 32; +var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); + var topLevelTypes = EventConstants.topLevelTypes; // Events and their corresponding property names. @@ -100,13 +103,48 @@ var BeforeInputEventPlugin = { var chars; - if (useBeforeInputEvent) { - // For browsers that support `textInput` events natively, don't do - // anything with keypress, composition, etc. - if (topLevelType !== topLevelTypes.topTextInput) { - return; + if (canUseTextInputEvent) { + switch (topLevelType) { + case topLevelTypes.topKeyPress: + /** + * If native `textInput` events are available, our goal is to make + * use of them. However, there is a special case: the spacebar key. + * In Webkit, preventing default on a spacebar `textInput` event + * cancels character insertion, but it *also* causes the browser + * to fall back to its default spacebar behavior of scrolling the + * page. + * + * Tracking at: + * https://code.google.com/p/chromium/issues/detail?id=355103 + * + * To avoid this issue, use the keypress event as if no `textInput` + * event is available. + */ + var which = nativeEvent.which; + if (which !== SPACEBAR_CODE) { + return; + } + + chars = String.fromCharCode(which); + break; + + case topLevelTypes.topTextInput: + // Record the characters to be added to the DOM. + chars = nativeEvent.data; + + // If it's a spacebar character, assume that we have already handled + // it at the keypress level and bail immediately. + if (chars === SPACEBAR_CHAR) { + return; + } + + // Otherwise, carry on. + break; + + default: + // For other native event types, do nothing. + return; } - chars = nativeEvent.data; } else { switch (topLevelType) { case topLevelTypes.topPaste: