You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React基于坐标定位元素:滚动后点击位置偏移问题求助

问题解决:页面滚动后点击定位错位

问题根源

你当前用clientX/clientY设置绝对定位元素的位置,这两个值是相对于浏览器视口的坐标。页面滚动时,文档内容相对于视口发生偏移,基于视口的坐标无法正确映射到文档的绝对位置,导致元素错位。

解决方案

推荐使用基于时间段容器的相对定位,让元素始终锚定在点击的容器区域内,不受页面滚动影响:

步骤1:给容器添加定位上下文和Ref

首先给时间段容器设置relative定位(作为绝对定位元素的父级上下文),并添加Ref用于获取容器位置:

import { useRef } from 'react';

// 组件内定义Ref
const timeSlotContainerRef = useRef<HTMLDivElement>(null);

// 修改容器的className和添加Ref
<div className="flex flex-col h-[93%] relative" ref={timeSlotContainerRef} onClick={handlePosClick}>
  {renderTimeSlots()}
</div>

步骤2:计算相对于容器的点击位置

修改handlePosClick,计算点击位置相对于容器的偏移量,而不是直接用视口坐标:

const handlePosClick = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!timeSlotContainerRef.current) return;
    
    // 获取容器在视口中的边界信息
    const containerRect = timeSlotContainerRef.current.getBoundingClientRect();
    // 计算点击位置相对于容器左上角的偏移
    const relativeLeft = e.clientX - containerRect.left;
    const relativeTop = e.clientY - containerRect.top - 20; // 保留原有的-20像素偏移

    setTimeSlotItems([...timeSlotItems, { 
        date: currentSelectedDate, 
        div: (
            <div 
                onClick={handleOpen} 
                className="w-[20%] h-[60px] bg-red-400 rounded-[1rem] absolute cursor-pointer" 
                style={{ left: `${relativeLeft}px`, top: `${relativeTop}px` }}
            ></div>
        )
    }])
}

步骤3:将红色元素移到容器内部

把红色div的渲染逻辑放到时间段容器内,确保它的绝对定位是相对于容器的:

<>
<div className="w-[72vw] -mr-[18vw] -mt-3 shadow-[0_3px_10px_rgb(0,0,0,0.2)] pl-5 pt-3 ml-5">
    <p className="text-black font-bold">{moment(currentSelectedDate.fullDate).subtract(1, 'day').format('ddd DD MMMM YYYY')}</p>

    <div className="flex flex-col h-[93%] relative" ref={timeSlotContainerRef} onClick={handlePosClick}>
      {renderTimeSlots()}
      {/* 红色元素放在容器内 */}
      {timeSlotItems.map((item: any) => {
        if (item.date === currentSelectedDate) return item.div
      })}
    </div>
</div>
</>

可选方案:改用文档坐标(仅适合元素固定在文档位置)

如果希望元素固定在文档的点击位置(滚动页面时元素不跟随视口),可以直接用pageX/pageY(相对于文档顶部的坐标)替换clientX/clientY:

const handlePosClick = (e: React.MouseEvent<HTMLDivElement>) => {
    setTimeSlotItems([...timeSlotItems, { 
        date: currentSelectedDate, 
        div: (
            <div 
                onClick={handleOpen} 
                className="w-[20%] h-[60px] bg-red-400 rounded-[1rem] absolute cursor-pointer" 
                style={{ left: `${e.pageX}px`, top: `${e.pageY - 20}px` }}
            ></div>
        )
    }])
}

内容的提问来源于stack exchange,提问作者ImDarkk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:50:28