diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 512e87b5..54d3fdb4 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -7,11 +7,11 @@ import { getNearestVNode, getNonDelegatedListenerMap, } from '../dom/DOMInternalKeys'; -import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; import {runDiscreteUpdates} from '../renderer/TreeBuilder'; import {isMounted} from '../renderer/vnode/VNodeUtils'; import {SuspenseComponent} from '../renderer/vnode/VNodeTags'; import {handleEventMain} from './HorizonEventMain'; +import {decorateNativeEvent} from './customEvents/EventFactory'; const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4); @@ -98,7 +98,7 @@ function isCaptureEvent(horizonEventName) { // 封装监听函数 function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { return event => { - const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement); + const customEvent = decorateNativeEvent(horizonEventName, nativeEvtName, event); listener(customEvent); }; } diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index deabece1..e099dda8 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -8,13 +8,12 @@ import { EVENT_TYPE_CAPTURE, } from './const'; import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler'; -import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler'; import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler'; import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler'; import { - addOnPrefix, + addOnPrefix, setPropertyWritable, } from './utils'; -import { createCustomEvent } from './customEvents/EventFactory'; +import { decorateNativeEvent } from './customEvents/EventFactory'; import { getListenersFromTree } from './ListenerGetter'; import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer'; @@ -47,7 +46,7 @@ function getCommonListeners( nativeEvtName = 'blur'; } - const horizonEvent = createCustomEvent(horizonEvtName, nativeEvtName, nativeEvent, target); + const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent); return getListenersFromTree( vNode, horizonEvtName, @@ -63,6 +62,8 @@ function processListeners(listenerList: ListenerUnitList): void { if (event.isPropagationStopped()) { return; } + + setPropertyWritable(event, 'currentTarget'); event.currentTarget = currentTarget; listener(event); event.currentTarget = null; @@ -105,17 +106,6 @@ function getProcessListeners( )); } - if (nativeEvtName === 'compositionend' || - nativeEvtName === 'compositionstart' || - nativeEvtName === 'compositionupdate') { - listenerList = listenerList.concat(getCompositionListeners( - nativeEvtName, - nativeEvent, - vNode, - target, - )); - } - if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) { listenerList = listenerList.concat(getBeforeInputListeners( nativeEvtName, diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index 8c20b960..53e14ee9 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -1,14 +1,13 @@ import {VNode} from '../renderer/Types'; import {DomComponent} from '../renderer/vnode/VNodeTags'; import {EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE} from './const'; -import {ListenerUnitList} from './Types'; -import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; +import {AnyNativeEvent, ListenerUnitList} from './Types'; // 获取监听事件 export function getListenersFromTree( targetVNode: VNode | null, horizonEvtName: string | null, - horizonEvent: CustomBaseEvent, + nativeEvent: AnyNativeEvent, eventType: string, ): ListenerUnitList { if (!horizonEvtName) { @@ -31,7 +30,7 @@ export function getListenersFromTree( vNode, listener: captureListener, currentTarget: realNode, - event: horizonEvent, + event: nativeEvent, }); } } @@ -43,7 +42,7 @@ export function getListenersFromTree( vNode, listener: bubbleListener, currentTarget: realNode, - event: horizonEvent, + event: nativeEvent, }); } } diff --git a/libs/horizon/src/event/Types.ts b/libs/horizon/src/event/Types.ts index b007bb2c..12071e36 100644 --- a/libs/horizon/src/event/Types.ts +++ b/libs/horizon/src/event/Types.ts @@ -1,6 +1,5 @@ import type {VNode} from '../renderer/Types'; -import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event; @@ -8,7 +7,7 @@ export type ListenerUnit = { vNode: null | VNode; listener: Function; currentTarget: EventTarget; - event: CustomBaseEvent; + event: AnyNativeEvent; }; export type ListenerUnitList = Array; diff --git a/libs/horizon/src/event/const.ts b/libs/horizon/src/event/const.ts index a3c2805e..7aebfa15 100644 --- a/libs/horizon/src/event/const.ts +++ b/libs/horizon/src/event/const.ts @@ -66,6 +66,9 @@ export const CommonEventToHorizonMap = { animationiteration: 'animationIteration', animationstart: 'animationStart', transitionend: 'transitionEnd', + compositionstart: 'compositionStart', + compositionend: 'compositionEnd', + compositionupdate: 'compositionUpdate', }; export const CHAR_CODE_ENTER = 13; diff --git a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts deleted file mode 100644 index 299a754d..00000000 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * 自定义的基本事件类型 - */ - -// 兼容IE的event key -const uniqueKeyMap = new Map([ - ['Esc', 'Escape'], - ['Spacebar', ' '], - ['Left', 'ArrowLeft'], - ['Up', 'ArrowUp'], - ['Right', 'ArrowRight'], - ['Down', 'ArrowDown'], - ['Del', 'Delete'], -]); - -// 从原生事件中复制属性到自定义事件中 -function extendAttribute(target, source) { - let val; - let attr; - for (attr in source) { - val = source[attr]; - if (val !== undefined) { - if (typeof val === 'function') { - let fun = source[attr]; - target[attr] = function() { - return fun.apply(source, arguments); - }; - } else { - target[attr] = val; - } - } - } -} - -export class CustomBaseEvent { - - isDefaultPrevented: () => boolean; - isPropagationStopped: () => boolean; - target: EventTarget | null; - - // 键盘事件属性 - key: string; - - // custom事件自定义属性 - customEventName: string; - type: string; - nativeEvent: any; - - constructor( - customEvtName: string, - nativeEvtName: string, - nativeEvt: { [propName: string]: any }, - target: EventTarget | null - ) { - // 复制原生属性到自定义事件 - extendAttribute(this, nativeEvt); - - this.isDefaultPrevented = () => nativeEvt.defaultPrevented; - this.isPropagationStopped = () => nativeEvt.cancelBubble; - this.target = target; - - // 键盘事件属性 - this.key = uniqueKeyMap.get(nativeEvt.key) || nativeEvt.key; - - // custom事件自定义属性 - this.customEventName = customEvtName; - this.type = nativeEvtName; - this.nativeEvent = nativeEvt; - } -} diff --git a/libs/horizon/src/event/customEvents/EventFactory.ts b/libs/horizon/src/event/customEvents/EventFactory.ts index c92a8aaa..db6bfc17 100644 --- a/libs/horizon/src/event/customEvents/EventFactory.ts +++ b/libs/horizon/src/event/customEvents/EventFactory.ts @@ -1,11 +1,33 @@ -import {CustomBaseEvent} from './CustomBaseEvent'; + +// 兼容IE的event key +const uniqueKeyMap = new Map([ + ['Esc', 'Escape'], + ['Spacebar', ' '], + ['Left', 'ArrowLeft'], + ['Up', 'ArrowUp'], + ['Right', 'ArrowRight'], + ['Down', 'ArrowDown'], + ['Del', 'Delete'], +]); // 创建普通自定义事件对象实例,和原生事件对应 -export function createCustomEvent(customEventName, nativeEvtName, nativeEvent, currentTarget) { - return new CustomBaseEvent( - customEventName, - nativeEvtName, - nativeEvent, - currentTarget, - ); +export function decorateNativeEvent(customEventName, nativeEvtName, nativeEvent) { + + nativeEvent.isDefaultPrevented = () => nativeEvent.defaultPrevented; + nativeEvent.isPropagationStopped = () => nativeEvent.cancelBubble; + + // custom事件自定义属性 + nativeEvent.customEventName = customEventName; + nativeEvent.nativeEvent = nativeEvent; + // 保存原生的事件类型,因为下面会修改 + nativeEvent.nativeEventType = nativeEvent.type; + + Object.defineProperty(nativeEvent, 'type', { writable: true }); + nativeEvent.type = nativeEvtName; + + const orgKey = nativeEvent.key; + Object.defineProperty(nativeEvent, 'key', { writable: true }); + nativeEvent.key = uniqueKeyMap.get(orgKey) || orgKey; + + return nativeEvent; } diff --git a/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts index 3d26a9a4..e05b175f 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts @@ -1,9 +1,8 @@ import type {VNode} from '../../renderer/Types'; import type {AnyNativeEvent} from '../Types'; import {getListenersFromTree} from '../ListenerGetter'; -import {createCustomEvent} from '../customEvents/EventFactory'; +import {decorateNativeEvent} from '../customEvents/EventFactory'; import {CHAR_CODE_SPACE, EVENT_TYPE_ALL} from '../const'; -import {CustomBaseEvent} from '../customEvents/CustomBaseEvent'; import {ListenerUnitList} from '../Types'; const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE); @@ -36,11 +35,10 @@ export function getListeners( return []; } - const event: CustomBaseEvent = createCustomEvent( + const event: AnyNativeEvent = decorateNativeEvent( 'onBeforeInput', 'beforeinput', nativeEvent, - target, ); event.data = chars; diff --git a/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts index 7a0ca02f..c0ad947d 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts @@ -1,4 +1,4 @@ -import {createCustomEvent} from '../customEvents/EventFactory'; +import {decorateNativeEvent} from '../customEvents/EventFactory'; import {getDom} from '../../dom/DOMInternalKeys'; import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler'; import {addValueUpdateList} from '../ControlledValueUpdater'; @@ -48,11 +48,10 @@ export function getListeners( // 判断是否需要触发change事件 if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) { addValueUpdateList(target); - const event = createCustomEvent( + const event = decorateNativeEvent( 'onChange', 'change', nativeEvt, - target, ); return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL); } diff --git a/libs/horizon/src/event/simulatedEvtHandler/CompositionEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/CompositionEventHandler.ts deleted file mode 100644 index 9dd8731c..00000000 --- a/libs/horizon/src/event/simulatedEvtHandler/CompositionEventHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type {VNode} from '../../renderer/Types'; -import type {AnyNativeEvent} from '../Types'; -import {getListenersFromTree} from '../ListenerGetter'; -import {createCustomEvent} from '../customEvents/EventFactory'; -import {EVENT_TYPE_ALL} from '../const'; -import {ListenerUnitList} from '../Types'; - -const compositionEventObj = { - compositionstart: 'onCompositionStart', - compositionend: 'onCompositionEnd', - compositionupdate: 'onCompositionUpdate', -}; - -// compoisition事件主要处理中文输入法输入时的触发事件 -export function getListeners( - nativeEvtName: string, - nativeEvt: AnyNativeEvent, - vNode: null | VNode, - target: null | EventTarget, -): ListenerUnitList { - const evtType = compositionEventObj[nativeEvtName]; - - const event = createCustomEvent( - evtType, - nativeEvtName, - nativeEvt, - target, - ); - return getListenersFromTree(vNode, evtType, event, EVENT_TYPE_ALL); -} diff --git a/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts index 778a3b82..cd5a09ad 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts @@ -1,9 +1,9 @@ -import {createCustomEvent} from '../customEvents/EventFactory'; +import {decorateNativeEvent} from '../customEvents/EventFactory'; import {shallowCompare} from '../../renderer/utils/compare'; import {getFocusedDom} from '../../dom/utils/Common'; import {getDom} from '../../dom/DOMInternalKeys'; import {isDocument} from '../../dom/utils/Common'; -import {isInputElement} from '../utils'; +import {isInputElement, setPropertyWritable} from '../utils'; import type {AnyNativeEvent} from '../Types'; import {getListenersFromTree} from '../ListenerGetter'; import type {VNode} from '../../renderer/Types'; @@ -54,12 +54,12 @@ function getSelectEvent(nativeEvent, target) { if (!shallowCompare(lastSelection, currentSelection)) { lastSelection = currentSelection; - const event = createCustomEvent( + const event = decorateNativeEvent( horizonEventName, 'select', nativeEvent, - target, ); + setPropertyWritable(nativeEvent, 'target'); event.target = currentElement; return getListenersFromTree( diff --git a/libs/horizon/src/event/utils.ts b/libs/horizon/src/event/utils.ts index 3909e7a8..d4b6f143 100644 --- a/libs/horizon/src/event/utils.ts +++ b/libs/horizon/src/event/utils.ts @@ -14,3 +14,10 @@ export function addOnPrefix(name) { } return 'on' + name[0].toUpperCase() + name.slice(1); } + +export function setPropertyWritable(obj, propName) { + const desc = Object.getOwnPropertyDescriptor(obj, propName); + if (!desc || !desc.writable) { + Object.defineProperty(obj, propName, { writable : true }); + } +}