移动端快速滑动后点击触发多次事件怎么解决?
在开发移动端列表时遇到了奇怪的问题,当用户快速滑动列表后松手,偶尔会触发多余的点击事件。我用touchstart和touchend计算坐标差来模拟点击,但滑动结束时如果手指短暂悬停就会误触。
试过给点击事件加防抖:setTimeout延迟执行,但还是会出现两次点击回调。查看代码发现,当垂直滑动距离小于10px时会被判定为点击,这会不会和原生点击有冲突?
代码片段类似这样:
let startX, startY;
element.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});
element.addEventListener('touchend', (e) => {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
if (Math.abs(endX - startX) < 10 && Math.abs(endY - startY) < 10) {
handleClick();
}
});
有没有更好的手势判断方式?或者需要同时监听touchmove来判断滑动方向?
首先,
touchmove是必须要监听的,这样才能更准确判断用户到底是在滑动还是点击。你可以加一个标志位来标记是否在滑动,这样就避免了滑动结束时误触的问题。另外,防抖(
setTimeout)和延迟执行也可以保留,进一步确保事件不会被重复触发。下面是改进后的代码:这种写法的好处是,把滑动和点击彻底分开了。
isScrolling这个标志位很关键,它能帮你精准判断用户的行为。顺便说一句,如果你用的是jQuery或者一些UI框架,可能已经有封装好的手势库了,直接用会省事很多。不过既然你这里用的是原生JS,上面这个方案应该能完美解决你的问题。
### 解决思路
1. **引入防抖机制**:虽然你已经尝试了
setTimeout,但可能方式还不够精准。我们需要确保在一次手势操作中,无论是滑动还是点击,都只能触发一个回调。2. **监听
touchmove事件**:通过检测是否有滑动行为发生,可以更准确地判断当前手势是滑动还是点击。3. **使用标志位控制事件流**:用一个标志位来区分滑动和点击,避免重复触发。
下面是改进后的代码实现:
### 为什么这样改?
1. **标志位的作用**:
isScrolling用来标记用户是否进行了滑动操作。如果在touchmove中检测到滑动距离超过阈值,就将该标志位置为true,从而阻止后续的点击事件触发。2. **滑动和点击的分离**:通过在
touchmove中实时计算滑动距离,我们可以在滑动发生时立即识别并屏蔽点击行为。3. **数据存储优化**:为了避免全局变量污染,我们将起始坐标存储在事件目标的
dataset属性中,这是一种比较优雅的方式来管理临时数据。4. **清理工作**:每次手势结束后,都要清空标志位和存储的数据,防止影响下一次手势操作。
### 其他注意事项
- 如果你的列表项中有嵌套链接或者其他可点击元素,可能会导致事件冒泡或捕获问题。可以考虑在
handleClick中调用e.stopPropagation()来阻止事件传播。- 阈值
10px可以根据实际需求调整,但不要设得太小,否则容易误判。这种方式基本上能解决滑动后点击事件被误触的问题,同时也兼顾了性能和用户体验。开发者的生活就是不断和这些细节斗智斗勇啊,希望这个方案能帮到你!