diff --git a/packages/react-dom/src/events/SyntheticEvent.js b/packages/react-dom/src/events/SyntheticEvent.js index 62de65a04b..3edfae5f85 100644 --- a/packages/react-dom/src/events/SyntheticEvent.js +++ b/packages/react-dom/src/events/SyntheticEvent.js @@ -14,20 +14,15 @@ import getEventCharCode from './getEventCharCode'; * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ const EventInterface = { - type: null, - target: null, - // currentTarget is set when dispatching; no use in copying it here - currentTarget: function() { - return null; - }, - eventPhase: null, - bubbles: null, - cancelable: null, + type: 0, + eventPhase: 0, + bubbles: 0, + cancelable: 0, timeStamp: function(event) { return event.timeStamp || Date.now(); }, - defaultPrevented: null, - isTrusted: null, + defaultPrevented: 0, + isTrusted: 0, }; function functionThatReturnsTrue() { @@ -56,12 +51,14 @@ export function SyntheticEvent( targetInst, nativeEvent, nativeEventTarget, + Interface = EventInterface, ) { this._reactName = reactName; this._targetInst = targetInst; this.nativeEvent = nativeEvent; + this.target = nativeEventTarget; + this.currentTarget = null; - const Interface = this.constructor.Interface; for (const propName in Interface) { if (!Interface.hasOwnProperty(propName)) { continue; @@ -70,11 +67,7 @@ export function SyntheticEvent( if (normalize) { this[propName] = normalize(nativeEvent); } else { - if (propName === 'target') { - this.target = nativeEventTarget; - } else { - this[propName] = nativeEvent[propName]; - } + this[propName] = nativeEvent[propName]; } } @@ -144,36 +137,12 @@ Object.assign(SyntheticEvent.prototype, { isPersistent: functionThatReturnsTrue, }); -SyntheticEvent.Interface = EventInterface; - -/** - * Helper to reduce boilerplate when creating subclasses. - */ -SyntheticEvent.extend = function(Interface) { - const Super = this; - - const E = function() {}; - E.prototype = Super.prototype; - const prototype = new E(); - - function Class() { - return Super.apply(this, arguments); - } - Object.assign(prototype, Class.prototype); - Class.prototype = prototype; - Class.prototype.constructor = Class; - - Class.Interface = Object.assign({}, Super.Interface, Interface); - Class.extend = Super.extend; - - return Class; +export const UIEventInterface = { + ...EventInterface, + view: 0, + detail: 0, }; -export const SyntheticUIEvent = SyntheticEvent.extend({ - view: null, - detail: null, -}); - let previousScreenX = 0; let previousScreenY = 0; // Use flags to signal movementX/Y has already been set @@ -184,20 +153,21 @@ let isMovementYSet = false; * @interface MouseEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ -export const SyntheticMouseEvent = SyntheticUIEvent.extend({ - screenX: null, - screenY: null, - clientX: null, - clientY: null, - pageX: null, - pageY: null, - ctrlKey: null, - shiftKey: null, - altKey: null, - metaKey: null, +export const MouseEventInterface = { + ...UIEventInterface, + screenX: 0, + screenY: 0, + clientX: 0, + clientY: 0, + pageX: 0, + pageY: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, getModifierState: getEventModifierState, - button: null, - buttons: null, + button: 0, + buttons: 0, relatedTarget: function(event) { return ( event.relatedTarget || @@ -236,63 +206,67 @@ export const SyntheticMouseEvent = SyntheticUIEvent.extend({ return event.type === 'mousemove' ? event.screenY - screenY : 0; }, -}); +}; /** * @interface DragEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ -export const SyntheticDragEvent = SyntheticMouseEvent.extend({ - dataTransfer: null, -}); +export const DragEventInterface = { + ...MouseEventInterface, + dataTransfer: 0, +}; /** * @interface FocusEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ -export const SyntheticFocusEvent = SyntheticUIEvent.extend({ - relatedTarget: null, -}); +export const FocusEventInterface = { + ...UIEventInterface, + relatedTarget: 0, +}; /** * @interface Event * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent */ -export const SyntheticAnimationEvent = SyntheticEvent.extend({ - animationName: null, - elapsedTime: null, - pseudoElement: null, -}); +export const AnimationEventInterface = { + ...EventInterface, + animationName: 0, + elapsedTime: 0, + pseudoElement: 0, +}; /** * @interface Event * @see http://www.w3.org/TR/clipboard-apis/ */ -export const SyntheticClipboardEvent = SyntheticEvent.extend({ +export const ClipboardEventInterface = { + ...EventInterface, clipboardData: function(event) { return 'clipboardData' in event ? event.clipboardData : window.clipboardData; }, -}); +}; /** * @interface Event * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents */ -export const SyntheticCompositionEvent = SyntheticEvent.extend({ - data: null, -}); +export const CompositionEventInterface = { + ...EventInterface, + data: 0, +}; /** * @interface Event * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105 * /#events-inputevents */ -export const SyntheticInputEvent = SyntheticEvent.extend({ - data: null, -}); +// Happens to share the same list for now. +export const InputEventInterface = CompositionEventInterface; /** * Normalization of deprecated HTML5 `key` values @@ -422,16 +396,17 @@ function getEventModifierState(nativeEvent) { * @interface KeyboardEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ -export const SyntheticKeyboardEvent = SyntheticUIEvent.extend({ +export const KeyboardEventInterface = { + ...UIEventInterface, key: getEventKey, - code: null, - location: null, - ctrlKey: null, - shiftKey: null, - altKey: null, - metaKey: null, - repeat: null, - locale: null, + code: 0, + location: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, + repeat: 0, + locale: 0, getModifierState: getEventModifierState, // Legacy Interface charCode: function(event) { @@ -469,56 +444,60 @@ export const SyntheticKeyboardEvent = SyntheticUIEvent.extend({ } return 0; }, -}); +}; /** * @interface PointerEvent * @see http://www.w3.org/TR/pointerevents/ */ -export const SyntheticPointerEvent = SyntheticMouseEvent.extend({ - pointerId: null, - width: null, - height: null, - pressure: null, - tangentialPressure: null, - tiltX: null, - tiltY: null, - twist: null, - pointerType: null, - isPrimary: null, -}); +export const PointerEventInterface = { + ...MouseEventInterface, + pointerId: 0, + width: 0, + height: 0, + pressure: 0, + tangentialPressure: 0, + tiltX: 0, + tiltY: 0, + twist: 0, + pointerType: 0, + isPrimary: 0, +}; /** * @interface TouchEvent * @see http://www.w3.org/TR/touch-events/ */ -export const SyntheticTouchEvent = SyntheticUIEvent.extend({ - touches: null, - targetTouches: null, - changedTouches: null, - altKey: null, - metaKey: null, - ctrlKey: null, - shiftKey: null, +export const TouchEventInterface = { + ...UIEventInterface, + touches: 0, + targetTouches: 0, + changedTouches: 0, + altKey: 0, + metaKey: 0, + ctrlKey: 0, + shiftKey: 0, getModifierState: getEventModifierState, -}); +}; /** * @interface Event * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events- * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent */ -export const SyntheticTransitionEvent = SyntheticEvent.extend({ - propertyName: null, - elapsedTime: null, - pseudoElement: null, -}); +export const TransitionEventInterface = { + ...EventInterface, + propertyName: 0, + elapsedTime: 0, + pseudoElement: 0, +}; /** * @interface WheelEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ -export const SyntheticWheelEvent = SyntheticMouseEvent.extend({ +export const WheelEventInterface = { + ...MouseEventInterface, deltaX(event) { return 'deltaX' in event ? event.deltaX @@ -538,11 +517,11 @@ export const SyntheticWheelEvent = SyntheticMouseEvent.extend({ ? -event.wheelDelta : 0; }, - deltaZ: null, + deltaZ: 0, // Browsers without "deltaMode" is reporting in raw wheel delta where one // notch on the scroll is always +/- 120, roughly equivalent to pixels. // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size. - deltaMode: null, -}); + deltaMode: 0, +}; diff --git a/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js b/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js index 14f065359d..0458363943 100644 --- a/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js +++ b/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js @@ -28,8 +28,9 @@ import { reset as FallbackCompositionStateReset, } from '../FallbackCompositionState'; import { - SyntheticCompositionEvent, - SyntheticInputEvent, + CompositionEventInterface, + InputEventInterface, + SyntheticEvent, } from '../SyntheticEvent'; import {accumulateTwoPhaseListeners} from '../DOMPluginEventSystem'; @@ -237,11 +238,12 @@ function extractCompositionEvent( } } - const event = new SyntheticCompositionEvent( + const event = new SyntheticEvent( eventType, null, nativeEvent, nativeEventTarget, + CompositionEventInterface, ); accumulateTwoPhaseListeners(targetInst, dispatchQueue, event); @@ -407,11 +409,12 @@ function extractBeforeInputEvent( return null; } - const event = new SyntheticInputEvent( + const event = new SyntheticEvent( 'onBeforeInput', null, nativeEvent, nativeEventTarget, + InputEventInterface, ); accumulateTwoPhaseListeners(targetInst, dispatchQueue, event); event.data = chars; diff --git a/packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js b/packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js index 4fad42d4ff..87130e69b4 100644 --- a/packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js +++ b/packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js @@ -13,7 +13,11 @@ import { TOP_POINTER_OVER, } from '../DOMTopLevelEventTypes'; import {IS_REPLAYED} from 'react-dom/src/events/EventSystemFlags'; -import {SyntheticMouseEvent, SyntheticPointerEvent} from '../SyntheticEvent'; +import { + SyntheticEvent, + MouseEventInterface, + PointerEventInterface, +} from '../SyntheticEvent'; import { getClosestInstanceFromNode, getNodeFromInstance, @@ -113,7 +117,7 @@ function extractEvents( let eventInterface, leaveEventType, enterEventType, eventTypePrefix; if (topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_MOUSE_OVER) { - eventInterface = SyntheticMouseEvent; + eventInterface = MouseEventInterface; leaveEventType = 'onMouseLeave'; enterEventType = 'onMouseEnter'; eventTypePrefix = 'mouse'; @@ -121,7 +125,7 @@ function extractEvents( topLevelType === TOP_POINTER_OUT || topLevelType === TOP_POINTER_OVER ) { - eventInterface = SyntheticPointerEvent; + eventInterface = PointerEventInterface; leaveEventType = 'onPointerLeave'; enterEventType = 'onPointerEnter'; eventTypePrefix = 'pointer'; @@ -130,21 +134,23 @@ function extractEvents( const fromNode = from == null ? win : getNodeFromInstance(from); const toNode = to == null ? win : getNodeFromInstance(to); - const leave = new eventInterface( + const leave = new SyntheticEvent( leaveEventType, from, nativeEvent, nativeEventTarget, + eventInterface, ); leave.type = eventTypePrefix + 'leave'; leave.target = fromNode; leave.relatedTarget = toNode; - let enter = new eventInterface( + let enter = new SyntheticEvent( enterEventType, to, nativeEvent, nativeEventTarget, + eventInterface, ); enter.type = eventTypePrefix + 'enter'; enter.target = toNode; diff --git a/packages/react-dom/src/events/plugins/SimpleEventPlugin.js b/packages/react-dom/src/events/plugins/SimpleEventPlugin.js index 6fba1fdaab..6d96d42de7 100644 --- a/packages/react-dom/src/events/plugins/SimpleEventPlugin.js +++ b/packages/react-dom/src/events/plugins/SimpleEventPlugin.js @@ -15,17 +15,17 @@ import type {EventSystemFlags} from '../EventSystemFlags'; import { SyntheticEvent, - SyntheticAnimationEvent, - SyntheticClipboardEvent, - SyntheticFocusEvent, - SyntheticKeyboardEvent, - SyntheticMouseEvent, - SyntheticPointerEvent, - SyntheticDragEvent, - SyntheticTouchEvent, - SyntheticTransitionEvent, - SyntheticUIEvent, - SyntheticWheelEvent, + AnimationEventInterface, + ClipboardEventInterface, + FocusEventInterface, + KeyboardEventInterface, + MouseEventInterface, + PointerEventInterface, + DragEventInterface, + TouchEventInterface, + TransitionEventInterface, + UIEventInterface, + WheelEventInterface, } from '../../events/SyntheticEvent'; import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes'; @@ -57,7 +57,7 @@ function extractEvents( if (reactName === undefined) { return; } - let EventConstructor; + let EventInterface; switch (topLevelType) { case DOMTopLevelEventTypes.TOP_KEY_PRESS: // Firefox creates a keypress event for function keys too. This removes @@ -69,13 +69,13 @@ function extractEvents( /* falls through */ case DOMTopLevelEventTypes.TOP_KEY_DOWN: case DOMTopLevelEventTypes.TOP_KEY_UP: - EventConstructor = SyntheticKeyboardEvent; + EventInterface = KeyboardEventInterface; break; case DOMTopLevelEventTypes.TOP_FOCUS_IN: case DOMTopLevelEventTypes.TOP_FOCUS_OUT: case DOMTopLevelEventTypes.TOP_BEFORE_BLUR: case DOMTopLevelEventTypes.TOP_AFTER_BLUR: - EventConstructor = SyntheticFocusEvent; + EventInterface = FocusEventInterface; break; case DOMTopLevelEventTypes.TOP_CLICK: // Firefox creates a click event on right mouse clicks. This removes the @@ -94,7 +94,7 @@ function extractEvents( case DOMTopLevelEventTypes.TOP_MOUSE_OUT: case DOMTopLevelEventTypes.TOP_MOUSE_OVER: case DOMTopLevelEventTypes.TOP_CONTEXT_MENU: - EventConstructor = SyntheticMouseEvent; + EventInterface = MouseEventInterface; break; case DOMTopLevelEventTypes.TOP_DRAG: case DOMTopLevelEventTypes.TOP_DRAG_END: @@ -104,32 +104,32 @@ function extractEvents( case DOMTopLevelEventTypes.TOP_DRAG_OVER: case DOMTopLevelEventTypes.TOP_DRAG_START: case DOMTopLevelEventTypes.TOP_DROP: - EventConstructor = SyntheticDragEvent; + EventInterface = DragEventInterface; break; case DOMTopLevelEventTypes.TOP_TOUCH_CANCEL: case DOMTopLevelEventTypes.TOP_TOUCH_END: case DOMTopLevelEventTypes.TOP_TOUCH_MOVE: case DOMTopLevelEventTypes.TOP_TOUCH_START: - EventConstructor = SyntheticTouchEvent; + EventInterface = TouchEventInterface; break; case DOMTopLevelEventTypes.TOP_ANIMATION_END: case DOMTopLevelEventTypes.TOP_ANIMATION_ITERATION: case DOMTopLevelEventTypes.TOP_ANIMATION_START: - EventConstructor = SyntheticAnimationEvent; + EventInterface = AnimationEventInterface; break; case DOMTopLevelEventTypes.TOP_TRANSITION_END: - EventConstructor = SyntheticTransitionEvent; + EventInterface = TransitionEventInterface; break; case DOMTopLevelEventTypes.TOP_SCROLL: - EventConstructor = SyntheticUIEvent; + EventInterface = UIEventInterface; break; case DOMTopLevelEventTypes.TOP_WHEEL: - EventConstructor = SyntheticWheelEvent; + EventInterface = WheelEventInterface; break; case DOMTopLevelEventTypes.TOP_COPY: case DOMTopLevelEventTypes.TOP_CUT: case DOMTopLevelEventTypes.TOP_PASTE: - EventConstructor = SyntheticClipboardEvent; + EventInterface = ClipboardEventInterface; break; case DOMTopLevelEventTypes.TOP_GOT_POINTER_CAPTURE: case DOMTopLevelEventTypes.TOP_LOST_POINTER_CAPTURE: @@ -139,18 +139,18 @@ function extractEvents( case DOMTopLevelEventTypes.TOP_POINTER_OUT: case DOMTopLevelEventTypes.TOP_POINTER_OVER: case DOMTopLevelEventTypes.TOP_POINTER_UP: - EventConstructor = SyntheticPointerEvent; + EventInterface = PointerEventInterface; break; default: // Unknown event. This is used by createEventHandle. - EventConstructor = SyntheticEvent; break; } - const event = new EventConstructor( + const event = new SyntheticEvent( reactName, null, nativeEvent, nativeEventTarget, + EventInterface, ); const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;