移动端 touch 事件延迟怎么解决?

A. 伊芃 阅读 8

我在做移动端的按钮点击功能,发现用 click 事件会有明显延迟,大概300ms左右,用户体验很差。听说要用 touch 事件来优化,但我试了 touchstart 又会触发多次,还和滚动冲突。

有没有靠谱的方案既能消除延迟,又不会误触?我试过引入 FastClick,但项目里已经有其他手势库了,怕冲突。能不能自己写个简单的处理逻辑?

element.addEventListener('touchstart', function(e) {
  e.preventDefault();
  // 执行点击逻辑
}, { passive: false });
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
端木鉴恒
这问题我之前也遇到过,最简单的方案其实不需要写一堆逻辑。

直接给你的按钮加个 CSS:touch-action: manipulation;,这玩意儿禁用了双击缩放,300ms 延迟直接就没了,click 事件秒触发。兼容性现在也很好,基本都支持。

如果你非要自己写 touch 事件处理,核心就是判断手指有没有移动,没移动才算点击:

function addTap(element, handler) {
let startX, startY;

element.addEventListener('touchstart', function(e) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}, { passive: true });

element.addEventListener('touchend', function(e) {
const touch = e.changedTouches[0];
const moveX = Math.abs(touch.clientX - startX);
const moveY = Math.abs(touch.clientY - startY);

// 移动超过 10px 就是滚动,不算点击
if (moveX < 10 && moveY < 10) {
handler.call(this, e);
}
}, { passive: true });
}


你原来的代码问题在于直接 e.preventDefault(),把滚动给禁了,当然冲突。
点赞
2026-03-01 16:09
小爱敏
小爱敏 Lv1
这个问题我之前踩过坑,300ms 延迟是浏览器为了等待双击缩放判断而故意留的。

先说个最简单的方案,如果你的目标用户是现代浏览器(iOS 10+、Android 5+),直接在 CSS 里加一行就完事了:

button, .clickable-element {
touch-action: manipulation;
}


这行代码的意思是禁用双击缩放,浏览器就不需要等那 300ms 了,click 事件会立即触发。调试看看效果,大部分场景这就够了。

如果你需要兼容老浏览器,自己写一个 tap 事件处理也行,核心思路是判断 touchstart 和 touchend 之间的时间差和位移,排除掉滚动和长按的情况:

function addTapEvent(element, handler) {
let startX = 0;
let startY = 0;
let startTime = 0;

element.addEventListener('touchstart', function(e) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
startTime = Date.now();
}, { passive: true });

element.addEventListener('touchend', function(e) {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const duration = Date.now() - startTime;
const moveX = Math.abs(endX - startX);
const moveY = Math.abs(endY - startY);

// 时间小于300ms,位移小于10px才算tap
if (duration < 300 && moveX < 10 && moveY < 10) {
handler.call(this, e);
}
}, { passive: true });
}


你原来的代码问题在于直接在 touchstart 里 preventDefault,这会把滚动也给干掉,页面就滚不动了。正确的做法是只在 touchend 里判断这是否是一次有效的点击行为。

还有个坑要注意,touchend 的时候如果手指滑出元素范围外,也不应该触发点击。可以加个判断:

element.addEventListener('touchend', function(e) {
const touch = e.changedTouches[0];
const target = document.elementFromPoint(touch.clientX, touch.clientY);

// 确保手指还在元素上
if (!element.contains(target)) {
return;
}
// 后续判断逻辑...
}, { passive: true });


说实话,如果项目没有特殊需求,touch-action: manipulation 是最省心的方案,性能也好,不用额外监听 touch 事件。实在不行再考虑自己封装 tap 事件。
点赞
2026-03-01 04:00