仅iOS移动设备上touchmove事件触发两次问题求助
问题现象
仅iOS系列移动设备(含Safari浏览器、iOS端WebView)上,执行单次滑动操作时,绑定在window上的touchmove事件会被触发两次,导致回调内逻辑重复执行,例如下方代码会连续弹出两次alert弹窗,安卓等其他设备无该异常:
$(window).bind('touchmove', function(e) { alert('something'); });
触发原因
- iOS部分版本(集中在iOS13~iOS16)的Web内核触摸事件派发机制存在特殊逻辑:滑动手势触发时,系统手势识别模块会先派发一次
touchmove事件用于滚动行为判定,判定完成后会再次向DOM树派发正式的触摸事件,两次事件都会冒泡到window层被监听到,且两次触发的时间差通常在10ms以内。 alert属于同步阻塞API,第一次事件回调触发弹窗时,第二次事件已经进入事件队列等待执行,关闭第一个弹窗后会立刻执行第二个回调弹出弹窗,让双触发问题被放大;如果回调是普通日志、DOM操作等非阻塞逻辑,该问题几乎不会被感知。- jQuery早期版本的
bind方法没有对同源瞬时重复事件做过滤,会完整响应两次事件触发。
修复方案
- 事件触发节流兜底(通用兼容方案,不影响原有页面逻辑)
通过时间戳判定过滤掉短时间内的重复触发,阈值设置为20ms即可覆盖iOS双触发的时间差,同时不会影响正常滑动时连续触发的touchmove事件(正常滑动的事件触发间隔约16ms/帧):
let lastTouchMoveStamp = 0; // 建议替换过时的bind为on绑定,jQuery1.7+版本均支持on方法 $(window).on('touchmove', function(e) { const currentStamp = Date.now(); // 拦截瞬时重复触发 if (currentStamp - lastTouchMoveStamp < 20) return; lastTouchMoveStamp = currentStamp; // 原有业务逻辑 alert('something'); });
- 阻止事件重复冒泡(适合单页无复杂嵌套滚动的场景)
在回调内拦截事件的重复冒泡传播,注意不要随意添加e.preventDefault(),否则会导致页面原生滚动失效:
$(window).on('touchmove', function(e) { e.stopPropagation(); // 仅全屏自定义滑动场景可开启下面这行,会禁用默认滚动 // e.preventDefault(); alert('something'); });
注意:不要通过移除事件监听再重新绑定的方式处理该问题,会导致正常滑动过程中的事件响应中断,出现滑动卡顿。
内容的提问来源于stack exchange,提问作者ibrahimnsall
相关产品推荐
相关产品推荐

