-
Notifications
You must be signed in to change notification settings - Fork 415
Expand file tree
/
Copy pathSyntheticEvent.ts
More file actions
155 lines (140 loc) · 3.75 KB
/
SyntheticEvent.ts
File metadata and controls
155 lines (140 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { Container } from './hostConfig';
import * as Scheduler from 'scheduler';
import {
SyncLane,
DefaultLane,
InputContinuousLane
} from 'react-reconciler/src/fiberLanes';
const { unstable_runWithPriority: runWithPriority } = Scheduler;
// 支持的事件类型
const validEventTypeList = ['click'];
export const elementPropsKey = '__props';
type EventCallback = (e: SyntheticEvent) => void;
interface Paths {
capture: EventCallback[];
bubble: EventCallback[];
}
interface SyntheticEvent extends Event {
__stopPropagation: boolean;
}
export interface DOMElement extends Element {
[elementPropsKey]: {
[key: string]: any;
};
}
function createSyntheticEvent(e: Event): SyntheticEvent {
const syntheticEvent = e as SyntheticEvent;
syntheticEvent.__stopPropagation = false;
const originStopPropagation = e.stopPropagation.bind(e);
syntheticEvent.stopPropagation = () => {
syntheticEvent.__stopPropagation = true;
if (originStopPropagation) {
originStopPropagation();
}
};
return syntheticEvent;
}
function getEventCallbackNameFromtEventType(
eventType: string
): string[] | undefined {
return {
click: ['onClickCapture', 'onClick']
}[eventType];
}
// 将支持的事件回调保存在DOM中
export const updateFiberProps = (node: DOMElement, props: any) => {
(node as DOMElement)[elementPropsKey] = props;
};
const triggerEventFlow = (paths: EventCallback[], se: SyntheticEvent) => {
for (let i = 0; i < paths.length; i++) {
const callback = paths[i];
runWithPriority(eventTypeToEventPriority(se.type), () => {
callback.call(null, se);
});
if (se.__stopPropagation) {
break;
}
}
};
const dispatchEvent = (container: Container, eventType: string, e: Event) => {
const targetElement = e.target;
if (targetElement === null) {
console.error('事件不存在target', e);
return;
}
const { capture, bubble } = collectPaths(
targetElement as DOMElement,
container,
eventType
);
const se = createSyntheticEvent(e);
if (__LOG__) {
console.log('模拟事件捕获阶段:', eventType);
}
triggerEventFlow(capture, se);
if (!se.__stopPropagation) {
if (__LOG__) {
console.log('模拟事件冒泡阶段:', eventType);
}
triggerEventFlow(bubble, se);
}
};
// 收集从目标元素到HostRoot之间所有目标回调函数
const collectPaths = (
targetElement: DOMElement,
container: Container,
eventType: string
): Paths => {
const paths: Paths = {
capture: [],
bubble: []
};
// 收集事件回调是冒泡的顺序
while (targetElement && targetElement !== container) {
const elementProps = targetElement[elementPropsKey];
if (elementProps) {
const callbackNameList = getEventCallbackNameFromtEventType(eventType);
if (callbackNameList) {
callbackNameList.forEach((callbackName, i) => {
const eventCallback = elementProps[callbackName];
if (eventCallback) {
if (i === 0) {
// 反向插入捕获阶段的事件回调
paths.capture.unshift(eventCallback);
} else {
// 正向插入冒泡阶段的事件回调
paths.bubble.push(eventCallback);
}
}
});
}
}
targetElement = targetElement.parentNode as DOMElement;
}
return paths;
};
export const initEvent = (container: Container, eventType: string) => {
if (!validEventTypeList.includes(eventType)) {
console.error('当前不支持', eventType, '事件');
return;
}
if (__LOG__) {
console.log('初始化事件:', eventType);
}
container.addEventListener(eventType, (e) => {
dispatchEvent(container, eventType, e);
});
};
const eventTypeToEventPriority = (eventType: string) => {
switch (eventType) {
case 'click':
case 'keydown':
case 'keyup':
return SyncLane;
case 'scroll':
return InputContinuousLane;
// TODO 更多事件类型
default:
return DefaultLane;
}
};