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

React移动端拖拽事件失效:复用桌面函数绑定触摸事件仍无效

React移动端触摸拖拽失效的问题修复

问题根源

你直接把桌面拖拽的处理函数复用给触摸事件,但触摸事件和桌面拖拽事件的机制完全不同,核心问题有两个:

  1. onTouchEnd触发时,ev.currentTarget不一定是手指最终离开的元素(可能手指滑动过程中已经移开原元素),导致无法正确获取放置目标
  2. 触摸事件需要通过触摸位置定位目标元素,而不是依赖事件的currentTarget

修正方案

1. 新增触摸状态跟踪

在原有状态基础上,添加触摸相关的状态变量:

const [dragId, setDragId] = useState(null);
const [array, setArray] = useState(jobsArray);
// 新增:跟踪触摸起始位置和起始元素
const [touchStartPos, setTouchStartPos] = useState({ x: 0, y: 0 });
const [touchTarget, setTouchTarget] = useState(null);

2. 编写独立的触摸事件处理函数

不能直接复用桌面拖拽的handleDrag和handleDrop,需要针对触摸事件重写逻辑:

const handleDragOver = (ev) => { ev.preventDefault() };

const handleDrag = (ev) => {
  setDragId(ev.currentTarget.id);
};

const handleDrop = (ev) => {
  const foundItem = array.find(job => job.title === dragId);
  const dragItem = array.find(job => job.title === ev.currentTarget.id);

  const filteredArray = array.filter(job => foundItem.title !== job.title);
  const elementPosition = array.indexOf(dragItem);
  const newArray = [...filteredArray.slice(0, elementPosition), foundItem, ...filteredArray.slice(elementPosition)];

  setArray(newArray);
};

// 触摸开始:记录起始位置和元素
const handleTouchStart = (ev) => {
  const touch = ev.touches[0];
  setTouchStartPos({ x: touch.clientX, y: touch.clientY });
  setTouchTarget(ev.currentTarget);
  setDragId(ev.currentTarget.id);
};

// 触摸移动:阻止默认滚动行为,可添加视觉反馈
const handleTouchMove = (ev) => {
  ev.preventDefault();
  // 可选:在这里添加元素跟随触摸的视觉效果
};

// 触摸结束:通过触摸位置找到实际放置目标
const handleTouchEnd = (ev) => {
  if (!touchTarget) return;

  const touch = ev.changedTouches[0];
  // 根据触摸结束的坐标找到对应的DOM元素
  const dropTarget = document.elementFromPoint(touch.clientX, touch.clientY);

  // 验证目标是我们的可拖拽项
  if (dropTarget?.classList.contains('dashboard-list')) {
    // 复用原有的数组重排逻辑
    const foundItem = array.find(job => job.title === dragId);
    const dragItem = array.find(job => job.title === dropTarget.id);

    const filteredArray = array.filter(job => foundItem.title !== job.title);
    const elementPosition = array.indexOf(dragItem);
    const newArray = [...filteredArray.slice(0, elementPosition), foundItem, ...filteredArray.slice(elementPosition)];

    setArray(newArray);
  }

  // 重置状态
  setTouchTarget(null);
  setDragId(null);
};

3. 更新元素的事件绑定

把新的触摸处理函数绑定到元素上:

<div 
  className="dashboard-list width-100 flex-center" 
  draggable 
  onDragOver={handleDragOver} 
  onDragStart={handleDrag} 
  onDrop={handleDrop}
  onTouchStart={handleTouchStart} 
  onTouchEnd={handleTouchEnd} 
  onTouchMove={handleTouchMove} 
  id={title}
>
  { content } 
</div>

额外说明

如果需要更流畅的拖拽体验,还可以在handleTouchMove中添加元素的位置偏移,让拖拽的元素跟随手指移动,增强视觉反馈。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:33:14