我在现有解决方案中遇到了以下问题,并找到了似乎可以解决所有问题的方法。这假设您的目标是跨浏览器、跨设备,并且不希望设备嗅探。
解决的问题
仅使用touchstart 或touchend:
- 当人们试图滚动浏览内容并且在他们开始滑动时碰巧将手指放在此元素上时触发事件 - 意外触发操作。
- 可能会导致事件在长按时触发,类似于在桌面上单击鼠标右键。例如,如果您的点击事件转到 URL X,并且用户长按以在新选项卡中打开 X,则用户会混淆地发现 X 在两个选项卡中都打开。在某些浏览器(例如 iPhone)上,它甚至可能会阻止长按菜单出现。
在touchstart 上触发mouseover 事件和在touchmove 上触发mouseout 的后果不太严重,但会干扰通常的浏览器行为,例如:
- 长按会触发永不结束的鼠标悬停。
- 许多 Android 浏览器将手指在
touchstart 上的位置视为mouseover,即下一个touchstart 上的mouseouted。因此,在 Android 中查看鼠标悬停内容的一种方法是触摸感兴趣的区域并摆动手指,轻轻滚动页面。将 touchmove 视为 mouseout 会破坏这一点。
解决方案
理论上,您可以使用touchmove 添加一个标志,但即使没有移动,iPhone 也会触发 touchmove。理论上,你可以比较touchstart和touchend事件pageX和pageYbut on iPhones, there's no touchend pageX or pageY。
不幸的是,要涵盖所有基础,它最终会变得更加复杂。
$el.on('touchstart', function(e){
$el.data('tstartE', e);
if(event.originalEvent.targetTouches){
// store values, not reference, since touch obj will change
var touch = e.originalEvent.targetTouches[0];
$el.data('tstartT',{ clientX: touch.clientX, clientY: touch.clientY } );
}
});
$el.on('touchmove', function(e){
if(event.originalEvent.targetTouches){
$el.data('tstartM', event.originalEvent.targetTouches[0]);
}
});
$el.on('click touchend', function(e){
var oldE = $el.data('tstartE');
if( oldE && oldE.timeStamp + 1000 < e.timeStamp ) {
$el.data('tstartE',false);
return;
}
if( $el.data('iosTouchM') && $el.data('tstartT') ){
var start = $el.data('tstartT'), end = $el.data('tstartM');
if( start.clientX != end.clientX || start.clientY != end.clientY ){
$el.data('tstartT', false);
$el.data('tstartM', false);
$el.data('tstartE',false);
return;
}
}
$el.data('tstartE',false);
理论上有ways to get the exact time used for a longpress instead of just using 1000 as an approximation, but in practice it's not that simple and it's best to use a reasonable proxy。