如何绑定“touchstart”和“点击”事件但不响应两者?

我正在开发一个可以在各种设备上工作的移动网站。 现在让我头痛的是黑莓手机。

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

理想情况下,我只是使用:

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

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

补救措施是改为使用touchstart:

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

但是我怎样才能约束这两个事件,但是只能发射一个呢? 我仍然需要键盘设备的点击事件,但是当然,如​​果我正在使用触摸设备,不希望点击事件触发。

一个额外的问题:是否有这样做,并附加适应甚至没有touchstart事件的浏览器? 在研究中,看起来像黑莓OS5不支持touchstart,所以也需要依靠点击事件的浏览器。

附录:

也许更全面的问题是:

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

理想情况下,答案是肯定的。 如果没有,我有一些select:

1)我们使用WURFL来获取设备信息,这样可以创build我们自己的设备matrix。 根据设备,我们将使用touchstart或点击。

2)通过JS在浏览器中检测触摸支持(我需要做一些更多的研究,但似乎是可行的)。

但是,这仍然留下一个问题:支持两者的设备呢? 我们支持的部分手机(即诺基亚手机和黑莓手机)同时拥有触摸屏键盘。 所以那种把我带回原来的问题……有没有一种方法可以立即允许两个?

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


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

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

这是我“创build”的修复,它取出了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 }); 

你可以尝试这样的事情:

 var clickEventType=((document.ontouchstart!==null)?'click':'touchstart'); $("#mylink").bind(clickEventType, myClickHandler); 

通常这也适用:

 $('#buttonId').on('touchstart click', function(e){ e.stopPropagation(); e.preventDefault(); //your code here }); 

我不得不做类似的事情。 这里是我工作的简化版本。 如果检测到触摸事件,请删除点击绑定。

 $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 }); 

只要joinreturn false; on("click touchstart")事件函数可以解决这个问题。

 $(this).on("click touchstart", function() { // Do things return false; }); 

从.on()上的jQuery文档

从事件处理程序返回false将自动调用event.stopPropagation()event.preventDefault() 。 也可以为处理函数传递一个false值作为function(){ return false; } function(){ return false; }

我以下面的方式成功了。

十分简单…

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

那么…所有这些都是非常复杂的。

如果你有modernizr,这是一个明智之举。

 ev = Modernizr.touch ? 'touchstart' : 'click'; $('#menu').on(ev, '[href="#open-menu"]', function(){ //winning }); 

一般来说,你不想混合默认的触摸和非触摸(点击)api。 一旦进入触摸世界,只需处理与触摸相关的function就容易了。 下面是一些伪代码,可以做你想做的事情。

如果在touchmove事件中连接并跟踪位置,则可以在doTouchLogic函数中添加更多项以检测手势和其他事件。

 var touchStartTime; var touchStartLocation; var touchEndTime; var touchEndLocation; $thing.bind('touchstart'), function() { var d = new Date(); touchStartTime = d.getTime(); touchStartLocation = mouse.location(x,y); }); $thing.bind('touchend'), function() { var d = new Date(); touchEndTime= d.getTime(); touchEndLocation= mouse.location(x,y); doTouchLogic(); }); function doTouchLogic() { var distance = touchEndLocation - touchStartLocation; var duration = touchEndTime - touchStartTime; if (duration <= 100ms && distance <= 10px) { // Person tapped their finger (do click/tap stuff here) } if (duration > 100ms && distance <= 10px) { // Person pressed their finger (not a quick tap) } if (duration <= 100ms && distance > 10px) { // Person flicked their finger } if (duration > 100ms && distance > 10px) { // Person dragged their finger } } 

我相信现在最好的做法是使用:

 $('#object').on('touchend mouseup', function () { }); 

touchend

当触摸点从触摸表面移除时触发事件被触发。

touchend事件不会触发任何鼠标事件。


鼠标松开

当鼠标指针位于元素上方并释放鼠标button时,mouseup事件被发送到元素。 任何HTML元素都可以接收此事件。

mouseup事件不会触发任何触摸事件。

 $('#click').on('mouseup', function () { alert('Event detected'); }); $('#touch').on('touchend', function () { alert('Event detected'); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <h1 id="click">Click me</h1> <h1 id="touch">Touch me</h1> 

另一个实施更好的维护。 不过,这个技巧也会做event.stopPropagation()。 点击不会被任何其他元素点击100毫秒。

 var clickObject = { flag: false, isAlreadyClicked: function () { var wasClicked = clickObject.flag; clickObject.flag = true; setTimeout(function () { clickObject.flag = false; }, 100); return wasClicked; } }; $("#myButton").bind("click touchstart", function (event) { if (!clickObject.isAlreadyClicked()) { ... } } 

只是出于文档的目的,这是我所做的最快/最响应的点击桌面/点击移动解决scheme,我可以想到的:

我用一个修改后的functionreplace了jQuery,只要浏览器支持触摸事件,用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); }; }()), }); 

用法与以前完全一样,比如:

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

但它会使用touchstart的时候可用,点击时不。 D.不需要任何延迟

我刚刚想出了这个想法,记住是否触发了ontouchstart 。 在这种情况下,我们在一个支持它的设备上,并且要忽略onclick事件。 由于ontouchstart 之前应该总是触发ontouchstart ,我使用这个:

 <script> touchAvailable = false; </script> <button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button> 

你可以尝试这样的:

 var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click'); $("#mylink").on(clickEvent, myClickHandler); 

这工作对我来说,手机听两者,所以防止一个,这是触摸事件。 桌面只听鼠标。

  $btnUp.bind('touchstart mousedown',function(e){ e.preventDefault(); if (e.type === 'touchstart') { return; } var val = _step( _options.arrowStep ); _evt('Button', [val, true]); }); 

在我的情况下,这完美的工作:

 jQuery(document).on('mouseup keydown touchend', function (event) { var eventType = event.type; if (eventType == 'touchend') { jQuery(this).off('mouseup'); } }); 

主要的问题是,当鼠标点击时,我尝试点击,在触摸设备触发点击和touchend在同一时间,如果我使用点击,一些function并没有在移动设备上工作。 点击的问题是,这是一个全球性事件,触发事件的其余部分,包括touchend。

我也在开发一个Android / iPad的networking应用程序,似乎只要使用“touchmove”就足以“移动组件”(不需要touchstart)。 通过禁用touchstart,您可以使用.click(); 来自jQuery。 它实际上是工作的,因为它没有被touchstart超载。

最后,你可以binb .live(“touchstart”,function(e){e.stopPropagation();}); 要求touchstart事件停止传播,客厅点击()来触发。

它为我工作。

试图解决这个问题有很多事情要考虑。 大多数解决scheme要么中断滚动,要么不正确地处理幻影点击事件。

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

注意:你不能在每个元素的基础上处理鬼点击事件。 一个延迟的点击是由屏幕位置触发的,所以如果你的触摸事件以某种方式修改了页面,点击事件将被发送到新的页面版本。

分配事件'touchstart mousedown''touchend mouseup'可能是有效的,以避免使用click产生不希望的副作用。

利用点击将始终跟随触摸事件的优势,这是我做了什么来摆脱“鬼点击”,而不必使用超时或全局标志。

 $('#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 Event API?

http://learn.jquery.com/events/event-extensions/

我用这个简单的事件成功了。 这是干净的,命名空间和足够灵活,以改善。

 var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent); var eventType = isMobile ? "touchstart" : "click"; jQuery.event.special.touchclick = { bindType: eventType, delegateType: eventType }; 

如果你正在使用jQuery,以下工作对我来说很好:

 var callback; // Initialize this to the function which needs to be called $(target).on("click touchstart", selector, (function (func){ var timer = 0; return function(e){ if ($.now() - timer < 500) return false; timer = $.now(); func(e); } })(callback)); 

其他解决scheme也很好,但我在一个循环中绑定多个事件,并需要自我调用函数来创build一个适当的闭包。 另外,我不想禁用绑定,因为我希望它可以在下次点击/触摸启动时调用。

可能帮助有类似情况的人!

对于简单的function,只需识别触摸或单击我使用下面的代码:

 var element = $("#element"); element.click(function(e) { if(e.target.ontouchstart !== undefined) { console.log( "touch" ); return; } console.log( "no touch" ); }); 

如果touchstart事件被定义,这将返回“touch”,否则返回“不触摸”。 就像我说的那样,这是一个简单的点击/点击事件的方法。

我正在尝试这个,到目前为止它的工作原理(但我只是在Android / PhoneGap的,所以警惕空心)

  function filterEvent( ob, ev ) { if (ev.type == "touchstart") { ob.off('click').on('click', function(e){ e.preventDefault(); }); } } $('#keypad').on('touchstart click', '.number, .dot', function(event) { filterEvent( $('#keypad'), event ); console.log( event.type ); // debugging only ... finish handling touch events... } 

我不喜欢这样一个事实,即我每次接触都是重新绑定处理程序,但所有被认为触及的事情都不会经常发生(在计算机时间内)!

我有一个像“#keypad”那样的处理程序,所以有一个简单的函数可以让我处理这个问题,而不需要太多的代码,这就是为什么我这样做的原因。

对于Mottie给出的最好的答案,我只是想让他的代码更加可重用,所以这是我的贡献:

 bindBtn ("#loginbutton",loginAction); function bindBtn(element,action){ var flag = false; $(element).bind('touchstart click', function(e) { e.preventDefault(); if (!flag) { flag = true; setTimeout(function() { flag = false; }, 100); // do something action(); } return false; }); 

尝试使用jQuery Mobile Virtual Mouse (vmouse) Bindings 。 这是特别针对您的情况的虚拟活动:

 $thing.on('vclick', function(event){ ... }); 

http://api.jquerymobile.com/vclick/

浏览器支持列表: http : //jquerymobile.com/browser-support/1.4/

编辑:我以前的答案(根据在这个线程的答案)是不是去我的路。 我想要一个子菜单展开鼠标进入或触摸点击和折叠鼠标离开或另一个触摸点击。 由于鼠标事件通常是在触摸事件之后触发的,所以写同时支持触摸屏和鼠标input的事件监听器是非常棘手的。

jQuery插件: 触摸或鼠标

我写了一个名为“ Touch或者Mouse ”的jQuery插件(897字节缩小版),可以检测触摸屏或鼠标是否触发事件(没有testing触摸支持!)。 这可以同时支持触摸屏和鼠标,并将其事件完全分开。

这样,OP可以使用touchstarttouchend来快速响应触摸点击并click 由鼠标调用的点击。

示范

第一个必须做,即。 身体元素跟踪触摸事件:

 $(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上的插件。;

说明

  1. 这个插件需要被调用,即。 跟踪touchstarttouchend事件的body元素,这样touchend事件不必在触发器元素(即链接或button)上触发。 在这两个触摸事件之间,这个插件考虑通过触摸调用任何鼠标事件。
  2. touchend之后touchend鼠标事件,当touchend后在ghostEventDelay (选项,默认为1000ms)内touchend鼠标事件时,此插件认为触摸调用鼠标事件。
  3. 使用触摸屏单击某个元素时,该元素将获得:active状态。 这个mouseleave事件只有在元素失去这个状态之后才会被触发。 点击另一个元素。 由于这可能是在mouseenter事件发生之后的几秒(或几分钟!),所以这个插件会跟踪元素的最后一个mouseenter事件:如果最后一个mouseenter事件是通过touch调用的,那么下面的mouseleave事件也被认为是由触摸。

这是一个简单的方法来做到这一点:

 // 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 } }); 

基本上,将触发的第一个事件types保存到附加到根文档的jQuery数据对象中的“trigger”属性,并且只在事件types等于“trigger”中的值时执行。 在触摸设备上,事件链可能是“触发”,然后是“点击”。 但是,“click”处理程序将不会被执行,因为“click”与保存在“trigger”(“touchstart”)中的初始事件types不匹配。

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

这里有一个codepen你可以玩(尝试点击触摸设备上的button – 应该没有点击延迟): http : //codepen.io/thdoan/pen/xVVrOZ

我还将其实现为一个简单的jQuery插件,它也支持jQuery的后代通过传递select器string进行筛选:

 // 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,它的工作原理就是通过一些逻辑来确保它是一个真实的触摸事件,而不是误报。

 $("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 });