diff --git a/src/components/ScrollView/index.js b/src/components/ScrollView/index.js index e92e15e3..682d76dd 100644 --- a/src/components/ScrollView/index.js +++ b/src/components/ScrollView/index.js @@ -117,7 +117,7 @@ const ScrollView = React.createClass({ _handleContentOnLayout(e: Object) { const { width, height } = e.nativeEvent.layout - this.props.onContentSizeChange && this.props.onContentSizeChange(width, height) + this.props.onContentSizeChange(width, height) }, render() { diff --git a/src/components/View/index.js b/src/components/View/index.js index bd8d03bf..52ad4734 100644 --- a/src/components/View/index.js +++ b/src/components/View/index.js @@ -105,7 +105,7 @@ class View extends Component { const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => { const handler = this.props[handlerName] if (typeof handler === 'function') { - handlerProps[handlerName] = this._normalizeEventForHandler(handler) + handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName) } return handlerProps }, {}) @@ -125,12 +125,21 @@ class View extends Component { return createReactDOMComponent(props) } - _normalizeEventForHandler(handler) { - const callback = (e) => { + _normalizeEventForHandler(handler, handlerName) { + // Browsers fire mouse events after touch events. This causes the + // ResponderEvents and their handlers to fire twice for Touchables. + // Auto-fix this issue by calling 'preventDefault' to cancel the mouse + // events. + const shouldCancelEvent = handlerName.indexOf('onResponder') === 0 + + return (e) => { e.nativeEvent = normalizeNativeEvent(e.nativeEvent) - return handler(e) + const returnValue = handler(e) + if (shouldCancelEvent && e.cancelable) { + e.preventDefault() + } + return returnValue } - return callback } } diff --git a/src/modules/injectResponderEventPlugin.js b/src/modules/injectResponderEventPlugin.js index adbe2a8a..69ecc308 100644 --- a/src/modules/injectResponderEventPlugin.js +++ b/src/modules/injectResponderEventPlugin.js @@ -45,12 +45,6 @@ ResponderTouchHistoryStore.recordTouchTrack = (topLevelType, nativeEvent) => { if ((topLevelType === topMouseMove) && !ResponderTouchHistoryStore.touchHistory.touchBank.length) { return } - // Cancel mouse events that browsers fire after touch events - if (topLevelType === topTouchStart || topLevelType === topTouchMove || topLevelType === topTouchEnd) { - if (nativeEvent.target.getAttribute('href') !== undefined) { - nativeEvent.preventDefault() - } - } const normalizedEvent = normalizeNativeEvent(nativeEvent) originalRecordTouchTrack.call(ResponderTouchHistoryStore, topLevelType, normalizedEvent) diff --git a/src/modules/normalizeNativeEvent.js b/src/modules/normalizeNativeEvent.js index fa8d8c67..4602fd94 100644 --- a/src/modules/normalizeNativeEvent.js +++ b/src/modules/normalizeNativeEvent.js @@ -7,6 +7,7 @@ const normalizeTouches = (touches = []) => Array.prototype.slice.call(touches).m const locationY = touch.pageY - rect.top return { + _normalized: true, clientX: touch.clientX, clientY: touch.clientY, force: touch.force, @@ -32,10 +33,13 @@ function normalizeTouchEvent(nativeEvent) { const touches = normalizeTouches(nativeEvent.touches) const event = { + _normalized: true, changedTouches, - originalEvent: nativeEvent, pageX: nativeEvent.pageX, pageY: nativeEvent.pageY, + preventDefault: nativeEvent.preventDefault.bind(nativeEvent), + stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent), + stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent), target: nativeEvent.target, // normalize the timestamp // https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events @@ -56,6 +60,7 @@ function normalizeTouchEvent(nativeEvent) { function normalizeMouseEvent(nativeEvent) { const touches = [{ + _normalized: true, clientX: nativeEvent.clientX, clientY: nativeEvent.clientY, force: nativeEvent.force, @@ -70,13 +75,16 @@ function normalizeMouseEvent(nativeEvent) { timestamp: Date.now() }] return { + _normalized: true, changedTouches: touches, identifier: touches[0].identifier, locationX: nativeEvent.offsetX, locationY: nativeEvent.offsetY, - originalEvent: nativeEvent, pageX: nativeEvent.pageX, pageY: nativeEvent.pageY, + preventDefault: nativeEvent.preventDefault.bind(nativeEvent), + stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent), + stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent), target: nativeEvent.target, timestamp: touches[0].timestamp, touches: (nativeEvent.type === 'mouseup') ? [] : touches @@ -84,8 +92,8 @@ function normalizeMouseEvent(nativeEvent) { } function normalizeNativeEvent(nativeEvent) { - if (nativeEvent.originalEvent) { return nativeEvent } - const eventType = nativeEvent.type || (nativeEvent.originalEvent && nativeEvent.originalEvent.type) || '' + if (nativeEvent._normalized) { return nativeEvent } + const eventType = nativeEvent.type || '' const mouse = eventType.indexOf('mouse') >= 0 return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent) }