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

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

理想情况下,我会使用:

$thing.click(function(){...})

但我们遇到的问题是,一些黑莓设备从触摸到触发点击有一个非常恼人的延迟。

补救方法是使用touchstart:

$thing.bind('touchstart', function(event){...})

但是如何绑定两个事件,但只触发一个事件呢?对于键盘设备,我仍然需要click事件,但当然,如果我使用的是触摸设备,我不想让click事件触发。

一个额外的问题:有没有办法做到这一点,并额外适应那些甚至没有touchstart事件的浏览器?在研究中,看起来黑莓OS5不支持touchstart,因此也需要依赖于该浏览器的点击事件。

附录:

也许一个更全面的问题是:

使用jQuery,是否可能/建议使用相同的绑定同时处理触摸交互和鼠标交互?

理想情况下,答案是肯定的。如果不是,我确实有一些选择:

我们使用WURFL来获取设备信息,这样就可以创建我们自己的设备矩阵。根据设备的不同,我们将使用touchstart或click。 通过JS检测浏览器中的触摸支持(我需要做更多的研究,但这似乎是可行的)。

然而,还有一个问题:支持这两种功能的设备怎么办?我们支持的一些手机(即诺基亚和黑莓)既有触摸屏又有键盘。这让我又回到了最初的问题……有没有一种方法可以同时兼顾两者?


当前回答

利用点击总是会在触摸事件之后发生的事实,以下是我在不使用超时或全局标志的情况下消除“幽灵点击”的方法。

$('#buttonId').on('touchstart click', function(event){
    if ($(this).data("already")) {
        $(this).data("already", false);
        return false;
    } else if (event.type == "touchstart") {
        $(this).data("already", true);
    }
    //your code here
});

基本上,只要在元素上触发ontouchstart事件,就会设置一个标记,然后在点击到来时删除(并忽略)。

其他回答

出于文档的目的,以下是我所能想到的最快/响应最快的桌面点击/移动点击解决方案:

我用一个修改过的函数替换了jQuery的on函数,只要浏览器支持触摸事件,就用touchstart替换了我所有的点击事件。

$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
    on: (function(){
        var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
        return function( types, selector, data, fn, one ) {
            if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
            return this._on( types, selector, data, fn);
        };
    }()),
});

than的用法和以前完全一样,比如:

$('#my-button').on('click', function(){ /* ... */ });

但它会在可用时使用touchstart,在不可用时使用click。不需要任何形式的延误:D

这对我来说很有效,手机会同时监听两个,所以要防止触摸事件。桌面只听鼠标。

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });

这是修复,我“创建”,它拿出GhostClick和实现FastClick。你自己试试,让我们知道它是否对你有效。

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic 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/

我不得不做一些类似的事情。下面是一个对我有用的简化版本。如果检测到触摸事件,则删除单击绑定。

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

在我的例子中,点击事件被绑定到<a>元素,所以我必须删除点击绑定并重新绑定点击事件,这阻止了<a>元素的默认操作。

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});