我正在开发一个需要在多种设备上运行的移动网站。目前让我头疼的是黑莓手机。
我们需要同时支持键盘点击和触摸事件。
理想情况下,我会使用:
$thing.click(function(){...})
但我们遇到的问题是,一些黑莓设备从触摸到触发点击有一个非常恼人的延迟。
补救方法是使用touchstart:
$thing.bind('touchstart', function(event){...})
但是如何绑定两个事件,但只触发一个事件呢?对于键盘设备,我仍然需要click事件,但当然,如果我使用的是触摸设备,我不想让click事件触发。
一个额外的问题:有没有办法做到这一点,并额外适应那些甚至没有touchstart事件的浏览器?在研究中,看起来黑莓OS5不支持touchstart,因此也需要依赖于该浏览器的点击事件。
附录:
也许一个更全面的问题是:
使用jQuery,是否可能/建议使用相同的绑定同时处理触摸交互和鼠标交互?
理想情况下,答案是肯定的。如果不是,我确实有一些选择:
我们使用WURFL来获取设备信息,这样就可以创建我们自己的设备矩阵。根据设备的不同,我们将使用touchstart或click。
通过JS检测浏览器中的触摸支持(我需要做更多的研究,但这似乎是可行的)。
然而,还有一个问题:支持这两种功能的设备怎么办?我们支持的一些手机(即诺基亚和黑莓)既有触摸屏又有键盘。这让我又回到了最初的问题……有没有一种方法可以同时兼顾两者?
我在那里给出了一个答案,我用jsfiddle演示。
您可以检查不同的设备并报告。
基本上,我使用了一种事件锁和一些服务于它的函数:
/*
* Event lock functions
* ====================
*/
function getEventLock(evt, key){
if(typeof(eventLock[key]) == 'undefined'){
eventLock[key] = {};
eventLock[key].primary = evt.type;
return true;
}
if(evt.type == eventLock[key].primary)
return true;
else
return false;
}
function primaryEventLock(evt, key){
eventLock[key].primary = evt.type;
}
然后,在我的事件处理程序中,我从对我的锁的请求开始:
/*
* Event handlers
* ==============
*/
$("#add").on("touchstart mousedown", addStart);
$("#add").on("touchend mouseup", addEnd);
function addStart(evt){
// race condition between 'mousedown' and 'touchstart'
if(!getEventLock(evt, 'add'))
return;
// some logic
now = new Date().getTime();
press = -defaults.pressDelay;
task();
// enable event lock and(?) event repetition
pids.add = setTimeout(closure, defaults.pressDelay);
function closure(){
// some logic(?): comment out to disable repetition
task();
// set primary input device
primaryEventLock(evt, 'add');
// enable event repetition
pids.add = setTimeout(closure, defaults.pressDelay);
}
}
function addEnd(evt){
clearTimeout(pids.add);
}
我必须强调的是,问题不在于简单地对一个事件做出回应,而在于对两个事件都不做出回应。
最后,在jsfiddle中有一个更新版本的链接,我通过在事件开始和结束处理程序中添加一个对事件锁库的简单调用以及2个范围变量eventLock和eventLockDelay,将对现有代码的影响最小化。
这里没有提到,但你可能想看看这个链接:https://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the-same-element/
为了便于后人回顾,您可以简单地检查设备是否是触摸屏并仅分配给相关事件,而不是尝试分配给两个处理程序然后对结果进行排序。观察:
var clickEvent = (function() {
if ('ontouchstart' in document.documentElement === true)
return 'touchstart';
else
return 'click';
})();
// and assign thusly:
el.addEventListener( clickEvent, function( e ){
// things and stuff
});
我使用它来绑定我的事件,这样我就可以在触摸屏上测试,处理touchstart和click事件,会触发两次,在我的开发PC上只听到点击
不过,该链接的作者提到了一个问题,即设计用于处理这两种事件的触摸屏笔记本电脑:
I learned about a third device I was not considering, the touchscreen laptop. It’s a hybrid device that supports both touch and click events. Binding one event means only that event be supported. Does that mean someone with a touchscreen and mouse would have to explicitly touch because that’s the only event I am handling?
Binding touchstart and click seemed ideal to handle these hybrid devices. To keep the event from firing twice, I added e.stopPropagation() and e.preventDefault() to the callback functions. e.stopPropagation() stops events from “bubbling up” to their parents but also keeps a second event from firing. I included e.preventDefault() as a “just in case” but seems like it could be omitted.