为什么这个点击事件是为一个不相关的元素触发的?

Why is this click event triggered for an unrelated element?

我正在处理这个工具提示,如果您将鼠标悬停在它上面,它会显示一个工具提示:

但是如果你点击它(用你的手指)它会全屏显示(移动支持):

代码如下所示:

export default function Tooltip({ message, children }: Props) {
    const [showSmallTip, setShowSmallTip] = useState(false);
    const [showBigTap, setShowBigTip] = useState(false);
    const ref = useRef(null);
    const pos = useBoundingBox(ref);

    const handleMouseEnter = useCallback(() => {
        setShowSmallTip(true);
    }, [setShowSmallTip]);

    const handleMouseLeave = useCallback(() => {
        setShowSmallTip(false);
    }, [setShowSmallTip]);

    const handleTap = useCallback(() => {
        console.log("TAP!")
        setShowBigTip(true);
        setShowSmallTip(false);
    }, [setShowBigTip]);

    const closeFullscreen = useCallback((ev:MouseEvent<HTMLElement>) => {
        console.log('CLOSE!!!')
        ev.stopPropagation();
        setShowBigTip(false);
        setShowSmallTip(false);
    }, [setShowBigTip]);

    const onTap = useTap(handleTap);

    return <>
        <Wrapper ref={ref} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} {...onTap}>
            {children}
        </Wrapper>
        {showSmallTip ? <End container={SCROLL_ROOT}><StyledTooltip style={{ top: pos.bottom, left: (pos.left + pos.right) / 2 }}>{message}</StyledTooltip></End> : null}
        {showBigTap ? <End><FullscreenTip onClick={closeFullscreen}><FullscreenText>{message}</FullscreenText></FullscreenTip></End>:null}
    </>
}

其中 useTap 是:

export default function useTap<T = Element>(callback: VoidCallback, options?: Options): TouchEvents<T> {
    const data = useRef<TouchData>(Object.create(null));

    return useMemo<TouchEvents<T>>(() => {
        const opt = { ...DEFAULT_OPTIONS, ...options } as Required<Options>;

        return {
            onTouchStart(ev) {
                data.current = {
                    time: ev.timeStamp,
                    x: ev.changedTouches[0].screenX,
                    y: ev.changedTouches[0].screenY,
                }
            },
            onTouchEnd(ev) {
                const mx = ev.changedTouches[0].screenX - data.current.x;
                const my = ev.changedTouches[0].screenY - data.current.y;
                const moved = Math.sqrt(mx**2 + my**2);
                const elapsed = ev.timeStamp - data.current.time;
                if (moved < opt.moveThreshold && elapsed < opt.pressDelay) {
                    // setTimeout(() => {
                        callback();
                    // }, 0);
                }
            }
        }
    }, [callback, options])
}

我遇到的问题是,当您点击图标时,它会立即打开并关闭全屏工具提示。即,它打印

TAP!
CLOSE!!!

轻轻一按。

现在我知道 touchendclick 之前触发,但我不明白的是为什么这会很重要?如果您查看我对 {...onTap}onClick={closeFullscreen} 处理程序的放置,它们是兄弟姐妹。事件不应该那样冒泡(在原生 DOM 和 React's VDOM 中都不是),而且我当然没有点击 <FullscreenTip> 所以 closeFullscreen 到底是怎么触发的?

<End>是传送门。

DEMO

尝试在 onTouchEnd 上致电 ev.preventDefault()

根据 spec touchend 事件是 "cancelable" 这意味着您可以使用 .preventDefault() 来防止鼠标事件.

If the preventDefault method is called on this event, it should prevent any default actions caused by any touch events associated with the same active touch point, including mouse events or scrolling.