如何绑定'touchstart'和'click'事件但不对两者同时响应?

217
我正在开发一个移动网站,它需要在各种设备上运行。目前让我头疼的是黑莓设备。
我们需要支持键盘点击和触摸事件。
理想情况下,我只需使用:
$thing.click(function(){...})

但我们遇到的问题是,一些黑莓设备在触摸屏幕后触发点击的时间会有非常烦人的延迟。

解决方法是改用touchstart:

$thing.bind('touchstart', function(event){...})
但是我该如何绑定两个事件,但只触发其中一个?我仍然需要 click 事件来支持键盘设备,但当然,如果我使用的是触摸设备,则不希望 click 事件被触发。

附加问题:是否有办法同时适应甚至没有 touchstart 事件的浏览器?研究后发现,BlackBerry OS5 不支持 touchstart 事件,因此也需要依赖 click 事件。

补充:

也许更全面的问题是:

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

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

  1. 我们可以使用 WURFL 来获取设备信息,因此可以创建自己的设备矩阵。根据设备,我们将使用 touchstart 或 click。
  2. 通过 JS 检测浏览器是否支持触摸(我需要进行更多的研究,但似乎是可行的)。

但这仍然存在一个问题:对于同时支持触摸屏和键盘的设备怎么办呢?我们支持的一些手机(即诺基亚和黑莓)都具有触摸屏和键盘。因此,这让我又回到了最初的问题...是否有办法同时允许两者?


2
最好绑定touchstart和touchend事件,并在您的触摸逻辑旁编写自己的单击逻辑。内置的click回调函数不知道触摸操作。 - Justin808
@DA - 不,你根本不需要绑定到 .click() 回调函数。我会尝试用一些伪代码来写出答案。我手头没有触摸设备来编写真正的代码 :) - Justin808
啊,但是需要澄清的是,我仍然需要点击事件,因为会有使用非触摸设备访问此网站的人。 - DA.
2
使用.bind('touchstart mouseup')可以解决这个问题(基于下面的评论之一) - oriadam
请查看.one方法:http://api.jquery.com/one。 - oriadam
显示剩余4条评论
37个回答

0

我也在开发一个Android/iPad Web应用程序,通过使用"touchmove"就足以移动组件(无需touchstart)。如果禁用touchstart,您可以使用jQuery的.click()方法。实际上它是有效的,因为它没有被touchstart重载。

最后,您可以使用binb .live("touchstart", function(e) { e.stopPropagation(); });来阻止touchstart事件传播,从而允许click()被触发。

这对我有用。


你如何禁用 touchstart - punkrockbuddyholly
我相信你可以这样做:jQuery("#my_element").on('touchstart', function(e){e.preventDefault()}); - nembleton

0

在尝试解决此问题时需要考虑许多因素。大多数解决方案要么破坏滚动,要么不能正确处理幽灵点击事件。

有关完整的解决方案,请参见https://developers.google.com/mobile/articles/fast_buttons

NB:您无法按元素处理幽灵点击事件。屏幕位置会触发延迟点击,因此,如果您的触摸事件以某种方式修改页面,则单击事件将发送到页面的新版本。


0

编辑: 我之前的答案(基于本主题中的答案)对我来说不是正确的方法。我想要一个子菜单在鼠标进入或触摸点击时展开,并在鼠标离开或另一个触摸点击时折叠。由于鼠标事件通常在触摸事件之后被触发,因此编写支持触摸屏和鼠标输入同时的事件侦听器有点棘手。

jQuery插件:Touch Or Mouse

最终我编写了一个名为 "Touch Or Mouse"(897字节压缩后)的jQuery插件,可以检测事件是由触摸屏还是鼠标调用的(无需测试触摸支持!)。这使得支持触摸屏和鼠标同时成为可能,并完全分离它们的事件。

这样,OP就可以使用touchstarttouchend快速响应触摸点击,而click则仅由鼠标调用。

演示

首先,必须使body元素跟踪触摸事件:

$(document.body).touchOrMouse('init');

鼠标事件通常绑定在元素上,通过调用$body.touchOrMouse('get', e),我们可以找出该事件是由触摸屏还是鼠标触发的。

$('.link').click(function(e) {
  var touchOrMouse = $(document.body).touchOrMouse('get', e);

  if (touchOrMouse === 'touch') {
    // Handle touch click.
  }
  else if (touchOrMouse === 'mouse') {
    // Handle mouse click.
  }
}

http://jsfiddle.net/lmeurs/uo4069nh上查看插件的运行情况。

说明

这个插件需要在body元素上调用以跟踪touchstart和touchend事件,这样touchend事件就不必在触发元素(例如链接或按钮)上触发。在这两个触摸事件之间,此插件认为任何鼠标事件都是由触摸引起的。 仅在touchend后触发鼠标事件,并且当鼠标事件在ghostEventDelay(默认情况下为1000ms)之内在touchend后被触发时,此插件将认为鼠标事件是由触摸引起的。 在使用触摸屏单击元素时,该元素处于:active状态。只有在元素通过单击另一个元素失去此状态时才会触发mouseleave事件。由于这可能是在mouseenter事件触发后几秒钟(或几分钟!),因此此插件会跟踪元素的最后一个mouseenter事件:如果最后一个mouseenter事件是由触摸引起的,则还将考虑以下mouseleave事件是由触摸引起的。

0

查找文档滚动移动差异(包括水平和垂直方向)的触摸开始和触摸结束事件,如果其中一个大于1像素,则是移动而不是点击。

var touchstartverscrollpos , touchstarthorscrollpos;


    $('body').on('touchstart','.thumbnail',function(e){

        touchstartverscrollpos = $(document).scrollTop();
        touchstarthorscrollpos = $(document).scrollLeft();


    });



    $('body').on('touchend','.thumbnail',function(e){


        var touchendverscrollpos = $(document).scrollTop();
        var touchendhorscrollpos = $(document).scrollLeft();

        var verdiff = touchendverscrollpos - touchstartverscrollpos;
        var hordiff = touchendhorscrollpos - touchstarthorscrollpos;


        if (Math.abs(verdiff) <1 && Math.abs(hordiff)<1){

// do you own function () here 



            e.stopImmediatePropagation();

            return false;
        }

    });

0
我发现最好的方法是编写触摸事件并通过该事件以编程方式调用正常的点击事件。这样,您就拥有了所有正常的点击事件,然后只需要为所有触摸事件添加一个事件处理程序即可。对于每个要使其可触摸的节点,只需将“touchable”类添加到其中以调用触摸处理程序。使用Jquery时,可以通过一些逻辑来确保它是真正的触摸事件而不是误报。
$("body").on("touchstart", ".touchable", function() { //make touchable  items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
    $(".touchable").on("touchend", function(event) {
        var d2 = new Date();
        var n2 = d2.getTime();
        if (n2 - n1 <= 300) {
            $(event.target).trigger("click"); //dont do the action here just call real click handler
        }
    });
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});

-1

我不确定这是否适用于所有浏览器和设备。 我使用Google Chrome和Safari iOS进行了测试。

$thing.on('click || touchend', function(e){

});

OR运算符应该只触发第一个事件(在桌面上应该是点击,在iPhone上应该是touchend)。


-2

你可以使用计数器来替代超时:

var count = 0;
$thing.bind('touchstart click', function(){
  count++;
  if (count %2 == 0) { //count 2% gives the remaining counts when devided by 2
    // do something
  }

  return false
});

% 运算符返回将两个操作数(变量 count 和 2)相除后的余数。 - HerrWalter
在某些情况下,计数器不会增加,即在移开手指后,手指离开$thing - lmeurs
如果只有 touchstart 或 click 中的一个被触发,这将无法正常工作。单击将计数器增加到 1,然后接下来的点击不会执行“// do something”。 - Davey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接