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

移动端滚动react-big-calendar时误触月视图日期单元格事件问题

解决react-big-calendar移动端月视图滚动误触事件点击问题

问题分析

longPressThreshold 参数仅用于区分长按和点击操作,无法识别滚动过程中的触摸移动,因此会把滚动时的触摸结束动作误判为点击,触发 onSelectEvent 或 onSelectSlot。

解决方案

方案1:通过触摸位移判断过滤误触

在父容器上监听触摸事件,记录触摸起始位置和滚动状态,在回调中过滤掉滚动触发的操作:

import { useState } from "react";
import { Calendar, momentLocalizer } from "react-big-calendar";
import moment from "moment";
import events from "./events";
import "react-big-calendar/lib/css/react-big-calendar.css";

moment.locale("en-GB");
const localizer = momentLocalizer(moment);

export default function ReactBigCalendar() {
  const [eventsData, setEventsData] = useState([]);
  const [isScrolling, setIsScrolling] = useState(false);
  let touchStartY = 0;

  // 记录触摸起始位置
  const handleTouchStart = (e) => {
    touchStartY = e.touches[0].clientY;
    setIsScrolling(false);
  };

  // 判断是否处于滚动状态
  const handleTouchMove = (e) => {
    const touchCurrentY = e.touches[0].clientY;
    // 位移超过10px判定为滚动
    if (Math.abs(touchCurrentY - touchStartY) > 10) {
      setIsScrolling(true);
    }
  };

  const handleSelect = ({ start, end }) => {
    // 滚动状态下不执行回调逻辑
    if (isScrolling) return;
    alert("hello");
    console.log(start);
    console.log(end);
  };

  return (
    <div 
      className="App"
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
    >
      <Calendar
        longPressThreshold={50}
        views={["month"]}
        selectable
        localizer={localizer}
        defaultDate={new Date()}
        defaultView="month"
        events={eventsData}
        style={{ height: "1000px", overflowY: "scroll" }}
        onSelectEvent={handleSelect}
        onSelectSlot={handleSelect}
      />
    </div>
  );
}

方案2:自定义事件组件处理触摸逻辑

如果只需要针对事件卡片做防误触,可以自定义 eventComponent,在组件内部处理触摸事件:

import { useState } from "react";
import { Calendar, momentLocalizer } from "react-big-calendar";
import moment from "moment";
import events from "./events";
import "react-big-calendar/lib/css/react-big-calendar.css";

moment.locale("en-GB");
const localizer = momentLocalizer(moment);

// 自定义事件组件
const CustomEvent = ({ event, style }) => {
  let touchStartX = 0;
  let touchStartY = 0;

  const handleTouchStart = (e) => {
    touchStartX = e.touches[0].clientX;
    touchStartY = e.touches[0].clientY;
  };

  const handleTouchEnd = (e) => {
    const touchEndX = e.changedTouches[0].clientX;
    const touchEndY = e.changedTouches[0].clientY;
    const deltaX = Math.abs(touchEndX - touchStartX);
    const deltaY = Math.abs(touchEndY - touchStartY);

    // 位移超过阈值则判定为滚动,不触发点击
    if (deltaX > 10 || deltaY > 10) {
      e.stopPropagation();
      return;
    }

    // 正常点击逻辑
    alert("hello");
    console.log(event.start);
    console.log(event.end);
  };

  return (
    <div
      style={style}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
    >
      {event.title}
    </div>
  );
};

export default function ReactBigCalendar() {
  const [eventsData, setEventsData] = useState([]);

  return (
    <div className="App">
      <Calendar
        longPressThreshold={50}
        views={["month"]}
        selectable
        localizer={localizer}
        defaultDate={new Date()}
        defaultView="month"
        events={eventsData}
        style={{ height: "1000px", overflowY: "scroll" }}
        eventComponent={CustomEvent} // 使用自定义事件组件
        onSelectSlot={({ start, end }) => {
          // 同样可以给slot添加防误触逻辑
          alert("hello");
          console.log(start);
          console.log(end);
        }}
      />
    </div>
  );
}

注意事项

  • 位移阈值(示例中的10px)可以根据实际需求调整,数值太小可能仍有误触,太大可能影响正常点击。
  • 如果需要同时处理日期单元格(slot)的误触,可参考方案1的逻辑,给slot添加类似的触摸判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:16:02