跳到主要内容

enter leave event plugin

一、作用

二、注册事件

备注
function registerEvents() {
registerDirectEvent('onMouseEnter', ['mouseout', 'mouseover']);
registerDirectEvent('onMouseLeave', ['mouseout', 'mouseover']);
registerDirectEvent('onPointerEnter', ['pointerout', 'pointerover']);
registerDirectEvent('onPointerLeave', ['pointerout', 'pointerover']);
}

三、提取事件

备注
/**
* For almost every interaction we care about, there will be both a top-level
* `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
* we do not extract duplicate events. However, moving the mouse into the
* browser from outside will not fire a `mouseout` event. In this case, we use
* the `mouseover` top-level event.
*
* 对于我们关心的几乎每一种交互,都将触发一个顶层的 `mouseover` 和 `mouseout` 事件。只使用
* `mouseout`,以避免提取重复事件。但是,将鼠标从浏览器外移动进入时不会触发 `mouseout` 事件。
* 在这种情况下,我们使用 `mouseover` 顶层事件。
*/
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
) {
const isOverEvent =
domEventName === 'mouseover' || domEventName === 'pointerover';
const isOutEvent =
domEventName === 'mouseout' || domEventName === 'pointerout';

if (isOverEvent && !isReplayingEvent(nativeEvent)) {
// If this is an over event with a target, we might have already dispatched
// the event in the out event of the other target. If this is replayed,
// then it's because we couldn't dispatch against this target previously
// so we have to do it now instead.
// 如果这是一个带有目标的 over 事件,我们可能已经在另一个目标的 out 事件中分发过该事件。
// 如果这个事件被重放,那是因为之前我们无法针对这个目标进行分发
// 所以现在我们必须执行分发。
const related =
(nativeEvent as any).relatedTarget || (nativeEvent as any).fromElement;
if (related) {
// If the related node is managed by React, we can assume that we have
// already dispatched the corresponding events during its mouseout.
// 如果相关节点由 React 管理,我们可以假设在其 mouseout 事件期间我们已经分发了相应的事件
if (
getClosestInstanceFromNode(related) ||
isContainerMarkedAsRoot(related)
) {
return;
}
}
}

if (!isOutEvent && !isOverEvent) {
// Must not be a mouse or pointer in or out - ignoring.
// 不能是鼠标或指针进出——忽略。
return;
}

let win;
// TODO: why is this nullable in the types but we read from it?
// TODO: 为什么在类型中它是可空的,但我们却从中读取?
if ((nativeEventTarget as any).window === nativeEventTarget) {
// `nativeEventTarget` is probably a window object.
// `nativeEventTarget` 可能是一个 window 对象。
win = nativeEventTarget;
} else {
// TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
// 待办:查明为什么在 IE8 中 `ownerDocument` 有时是未定义的。
const doc = (nativeEventTarget as any).ownerDocument;
if (doc) {
win = doc.defaultView || doc.parentWindow;
} else {
win = window;
}
}

let from;
let to;
if (isOutEvent) {
const related = nativeEvent.relatedTarget || (nativeEvent as any).toElement;
from = targetInst;
to = related ? getClosestInstanceFromNode(related as any) : null;
if (to !== null) {
const nearestMounted = getNearestMountedFiber(to);
const tag = to.tag;
if (
to !== nearestMounted ||
(tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
) {
to = null;
}
}
} else {
// Moving to a node from outside the window.
// 从窗口外移动到一个节点。
from = null;
to = targetInst;
}

if (from === to) {
// Nothing pertains to our managed components.
// 与我们的托管组件无关。
return;
}

let SyntheticEventCtor = SyntheticMouseEvent;
let leaveEventType = 'onMouseLeave';
let enterEventType = 'onMouseEnter';
let eventTypePrefix = 'mouse';
if (domEventName === 'pointerout' || domEventName === 'pointerover') {
SyntheticEventCtor = SyntheticPointerEvent;
leaveEventType = 'onPointerLeave';
enterEventType = 'onPointerEnter';
eventTypePrefix = 'pointer';
}

const fromNode = from == null ? win : getNodeFromInstance(from);
const toNode = to == null ? win : getNodeFromInstance(to);

const leave: KnownReactSyntheticEvent = new SyntheticEventCtor(
leaveEventType,
eventTypePrefix + 'leave',
from,
nativeEvent,
nativeEventTarget,
);
leave.target = fromNode;
leave.relatedTarget = toNode;

let enter: KnownReactSyntheticEvent | null = null;

// We should only process this nativeEvent if we are processing
// the first ancestor. Next time, we will ignore the event.
// 我们应该只处理这个 nativeEvent,如果我们正在处理第一个祖先节点。下次,我们将忽略这个事件
const nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget as any);
if (nativeTargetInst === targetInst) {
const enterEvent: KnownReactSyntheticEvent = new SyntheticEventCtor(
enterEventType,
eventTypePrefix + 'enter',
to,
nativeEvent,
nativeEventTarget,
);
enterEvent.target = toNode;
enterEvent.relatedTarget = fromNode;
enter = enterEvent;
}

accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}