我正在开发一个需要在多种设备上运行的移动网站。目前让我头疼的是黑莓手机。

我们需要同时支持键盘点击和触摸事件。

理想情况下,我会使用:

$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,将对现有代码的影响最小化。

其他回答

我通过以下方法成功了。

容易Peasy……

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});

这里有一个简单的方法:

// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
  if (!$(document).data('trigger')) $(document).data('trigger', e.type);
  if (e.type===$(document).data('trigger')) {
    // Do your stuff here
  }
});

基本上,您可以将第一个被触发的事件类型保存到附加到根文档的jQuery数据对象中的'trigger'属性中,并且仅当事件类型等于'trigger'中的值时才执行。在触控设备上,事件链可能是“touchstart”后面跟着“click”;然而,“点击”处理程序将不会被执行,因为“点击”与保存在“触发器”(“touchstart”)中的初始事件类型不匹配。

假设,我相信这是一个安全的假设,即你的智能手机不会自发地从触摸设备转变为鼠标设备,否则点击将永远不会注册,因为“触发器”事件类型只保存一次页面加载和“点击”永远不会匹配“touchstart”。

这里有一个你可以玩的代码依赖(试着点击触摸设备上的按钮——应该没有点击延迟):http://codepen.io/thdoan/pen/xVVrOZ

我还实现了一个简单的jQuery插件,通过传递一个选择器字符串来支持jQuery的后代过滤:

// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
  var selector, handler;
  switch (typeof arg1) {
    case 'function':
      selector = null;
      handler = arg1;
      break;
    case 'string':
      selector = arg1;
      if (typeof arg2==='function') handler = arg2;
      else return;
      break;
    default:
      return;
  }
  this.on('click touchstart', selector, function(e) {
    if (!$(document).data('trigger')) $(document).data('trigger', e.type);
    if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
  });
};

Codepen: http://codepen.io/thdoan/pen/GZrBdo/

如果您已经在使用jQuery,有两种方法来处理这个问题,它们都非常简短。我认为它不需要像大多数答案那么复杂……

返回错误;

只需监听任何/所有事件,并在最后添加“return false;”以停止额外的重复事件处理。

thing$.on('click touchend',function(){
    // do Stuff
    return false; // stop
});

可重复使用的例子:

function bindClickAndTouchEvent(__targets$, __eventHandler){
    
    __targets$.on('click touchend',function(){
        __eventHandler.apply(this,arguments);// send original event
        return false;// but stop additional events
    });
}

// In-Line Usage:

bindClickAndTouchEvents( $('#some-element-id'), function(){ 
    console.log('Hey look only one click even when using touch on a touchscreen laptop') 
});

鼠标向上和触摸结束

根据您的用例,您可能只使用mouseup和touchend,因为这两个事件完全不重叠,只创建一个事件开始…然后你甚至不需要“return false;”。

thing$.on('mouseup touchend',function(){
    // do Stuff
});

更新:查看jQuery指针事件Polyfill项目,它允许您绑定到“指针”事件,而不是在鼠标和触摸之间选择。


绑定到两者,但要做一个标记,以便函数每100ms左右只触发一次。

var flag = false;
$thing.bind('touchstart click', function(){
  if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
    // do something
  }

  return false
});

我刚刚想出了一个想法,记住如果ontouchstart被触发。在本例中,我们在支持onclick事件的设备上,并希望忽略onclick事件。因为ontouchstart应该总是在onclick之前被触发,我使用这个:

<脚本> touchAvailable = false;> < /脚本 <按钮ontouchstart = " touchAvailable = true;myFunction();" onclick="if(!touchAvailable);按钮“>按钮< / >