React中touchend事件在移动端为什么没触发?

极客尚文 阅读 214

我在用React写移动端滑动删除功能时遇到问题,给元素绑定了touchstart和touchend事件,但结束触摸时事件没反应。手指离开屏幕后控制台只打印了touchstart,touchend完全没触发。

代码是这样写的:


import { useRef } from 'react';

function SwipeItem() {
  const itemRef = useRef(null);

  const handleTouchStart = (e) => {
    console.log('touchstart触发了');
  };

  const handleTouchEnd = () => {
    console.log('touchend为什么没触发?');
  };

  return (
    <div 
      ref={itemRef}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
      style={{ height: '100px', backgroundColor: 'lightgray' }}
    >
      可滑动项
    </div>
  );
}

已经试过在touchstart里加e.preventDefault(),但页面会出现无法滚动的问题。当手指快速滑动离开元素时事件更不靠谱,这是怎么回事?

我来解答 赞 9 收藏
二维码
手机扫码查看
2 条解答
长孙洋毅
touchend事件在移动端没触发,大概率是因为手指滑动的时候超出了元素范围,或者被浏览器默认行为干扰了。React里处理touch事件确实有点坑,改一下就行。

建议监听整个document的touchend,这样能确保手指离开屏幕时一定能捕获到。另外别忘了在组件卸载时清除事件监听,不然可能会导致内存泄漏。

下面是改好的代码:

import { useRef, useEffect } from 'react';

function SwipeItem() {
const itemRef = useRef(null);

const handleTouchStart = () => {
console.log('touchstart触发了');
};

const handleTouchEnd = () => {
console.log('touchend触发了');
};

useEffect(() => {
const currentElement = itemRef.current;

// 绑定document的touchend
const onTouchEnd = (e) => {
if (currentElement.contains(e.target)) {
handleTouchEnd();
}
};

document.addEventListener('touchend', onTouchEnd);

// 清理函数
return () => {
document.removeEventListener('touchend', onTouchEnd);
};
}, []);

return (
<div
ref={itemRef}
onTouchStart={handleTouchStart}
style={{ height: '100px', backgroundColor: 'lightgray' }}
>
可滑动项
</div>
);
}


这样写有几个好处:
- 不用担心手指滑出去事件丢失
- 避免阻止默认事件导致页面不能滚动
- 自动清理事件监听器

我之前也被这个坑过好几次,后来就养成习惯了,移动端touch事件直接监听document最保险。
点赞 1
2026-02-17 09:03
程序猿柯佳
这个问题很常见,React 中的 onTouchEnd 不触发通常是因为 touchend 的触发条件没有满足。移动设备上,touchend 是在所有触点都离开屏幕时触发的,但如果你在 touchstart 里没有正确处理默认行为,浏览器可能会直接放弃后续事件。

### 根本原因

touchend 没有触发通常是因为:
1. 手指快速滑动后离开屏幕时,浏览器认为这是一个滚动操作,不是完整的 touch 流程。
2. 如果你在 touchstart 中调用了 e.preventDefault(),但没有在 touchmove 中持续处理,会导致浏览器中断 touch 流程。

### 解决方案

推荐的做法是添加一个 onTouchMove 事件处理,用来「维持」触摸流程,并确保浏览器不会中断后续的 touchend

这是修改后的完整代码:

import { useRef } from 'react';

function SwipeItem() {
const itemRef = useRef(null);

const handleTouchStart = (e) => {
console.log('touchstart触发了');
// 可以记录初始位置用于后续判断是否是滑动
// e.touches[0].clientX 等
};

const handleTouchMove = (e) => {
// 加了这个,浏览器就会认为你在“处理”触摸,从而保留后续 touchend
e.preventDefault();
// 这里可以做滑动逻辑
};

const handleTouchEnd = () => {
console.log('touchend终于触发了');
// 这里可以判断滑动距离并决定是否删除
};

return (
<div
ref={itemRef}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
style={{ height: '100px', backgroundColor: 'lightgray' }}
>
可滑动项
</div>
);
}


### 注意事项

- onTouchMove 中的 e.preventDefault() 是关键,但要注意只在你真正需要拦截滚动的时候调用,否则可能会影响页面滚动。
- 推荐用 e.touchese.changedTouches 来记录触摸点变化,用于判断滑动方向或距离。
- 实际开发中推荐使用一些封装好的库(如 react-swipeable),它们已经处理了这些兼容性问题。

希望这能帮你把 touchend 拯救回来。React 的事件系统虽然方便,但有些底层细节还是要自己兜底 😅
点赞 3
2026-02-05 01:02