如何绑定 Mousedown 和 Touchstart,但不响应两者?安卓、jQuery

Posted

技术标签:

【中文标题】如何绑定 Mousedown 和 Touchstart,但不响应两者?安卓、jQuery【英文标题】:How to bind both Mousedown and Touchstart, but not respond to both? Android, JQuery 【发布时间】:2012-11-19 07:19:55 【问题描述】:

在一个也可以在移动设备上查看的网站上工作,并且需要在 touchstart 和 mousedown 上绑定一个操作。

看起来像这样

 $("#roll").bind("mousedown touchstart", function(event)

 someAction();

它在 Iphone 上运行良好,但在 android 上它响应两次。

event.stopPropagation();
event.preventDefault();

添加此代码修复了 Android Chrome 的问题,但不适用于 Android 默认浏览器。还有其他可以解决所有android问题的技巧吗?

【问题讨论】:

event.preventDefault() 应该停止 mousedown 事件。这个问题很老了,所以希望它现在得到解决。我遇到了同样的问题,结果发现是 jquery touchpunch 造成的。 这个相同的问题可能对某人的帮助不仅仅是在这里回答:***.com/questions/7018919/… 【参考方案1】:
element.on('touchstart mousedown', function(e) 
    e.preventDefault();
    someAction();
);

preventDefault 取消事件,as per specs

您会获得 touchstart,但一旦您取消它,您将不再获得 mousedown。与接受的答案相反,您无需致电stopPropagation,除非您需要。即使取消,该事件也会正常传播。浏览器会忽略它,但你的钩子仍然可以工作。

Mozilla agrees with me 在这个:

在 touchstart 或一系列的第一个 touchmove 事件上调用 preventDefault() 可防止相应的鼠标事件触发

编辑:我刚刚再次阅读了这个问题,你说你已经这样做了,它没有修复 Android 默认浏览器。不确定接受的答案有何帮助,因为它基本上做同样的事情,只是以更复杂的方式和一个事件传播错误(touchstart 不传播,但 click 传播)

【讨论】:

在 android 上,我得到“忽略取消可取消的 touchmove 事件的尝试=false,例如因为滚动正在进行中且无法中断” 你可能需要 addEventListener('touchstart', FUNCTION, passive: false); 可以,但是你甚至不能通过滑动来滚动页面。【参考方案2】:

我一直在使用这个功能:

//touch click helper
(function ($) 
    $.fn.tclick = function (onclick) 

        this.bind("touchstart", function (e)  
            onclick.call(this, e); 
            e.stopPropagation(); 
            e.preventDefault(); 
        );

        this.bind("click", function (e)  
           onclick.call(this, e);  //substitute mousedown event for exact same result as touchstart         
        );   

        return this;
    ;
)(jQuery);

更新:修改答案以同时支持鼠标和触摸事件。

【讨论】:

请注意,Windows 8 上的 Chrome 和 Firefox 将在检测到触摸屏时启用触摸事件。因此用户可以在鼠标和触摸输入之间切换。使用此代码,鼠标事件将被忽略。 @gregers 你是对的,也是错的。 Windows 8 确实支持 Touch 和 Mouse 事件,但是在触摸时您可以取消“虚拟”鼠标事件以仅处理触摸,它不会影响 REAL 鼠标事件,因为它们不会触发 Touch 事件。 @daniel.gindi 我的评论是针对此答案的先前版本:***.com/posts/14202543/revisions touchstart 等价于mousedown 而不是click。单击需要用户按下按钮。我看不出这有什么完全相同的结果。 您将如何解除此事件的绑定?说是否要暂时添加侦听器,然后停止触发事件?【参考方案3】:

考虑到 gregers 对 win8 和 chrome/firefox 的评论,skyisred 的评论看起来并没有那么愚蠢(:P @ all the haters) 虽然我宁愿使用黑名单而不是他建议的白名单,仅将 Android 排除在触摸绑定之外:

var ua = navigator.userAgent.toLowerCase(),
isAndroid = ua.indexOf("android") != -1,
supportsPointer = !!window.navigator.msPointerEnabled,
ev_pointer = function(e)  ... , // function to handle IE10's pointer events
ev_touch = function(e)  ... , // function to handle touch events
ev_mouse = function(e)  ... ; // function to handle mouse events

if (supportsPointer)  // IE10 / Pointer Events
    // reset binds
    $("yourSelectorHere").on('MSPointerDown MSPointerMove MSPointerUp', ev_pointer);
 else 
    $("yourSelectorHere").on('touchstart touchmove touchend', ev_touch); // touch events
    if(!isAndroid)  
        // in androids native browser mouse events are sometimes triggered directly w/o a preceding touchevent (most likely a bug)
        // bug confirmed in android 4.0.3 and 4.1.2
        $("yourSelectorHere").on('mousedown mousemove mouseup mouseleave', ev_mouse); // mouse events
    

顺便说一句:我发现鼠标事件并不总是被触发(如果使用了 stopPropagation 和 preventDefault),特别是我只注意到一个额外的 mousemove 直接在 touchend 事件之前......真的很奇怪的错误,但上面的代码为我修复了它跨所有平台(测试过的 OSX、Win、ios 5+6、Android 2+4,每个都带有本机浏览器、Chrome、Firefox、IE、Safari 和 Opera,如果可用)。

【讨论】:

这太冗长了,你能想象每次 IE 发布新版本时都要维护所有这些吗?【参考方案4】:

哇,这个问题和相关问题有这么多答案,但没有一个对我有用(Chrome、mobil responsive、mousedown + touchstart)。但是这个:

(e) => 
  if(typeof(window.ontouchstart) != 'undefined' && e.type == 'mousedown') return;

  // do anything...

【讨论】:

务实的解决方案:)【参考方案5】:

使用此代码修复

var mobile   = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent); 
var start = mobile ? "touchstart" : "mousedown";
$("#roll").bind(start, function(event)

【讨论】:

-1:使用特征检测(如 Joshua 的回答)而不是 UA 检测。 这是最好的答案,也是唯一有效的答案。约书亚的回答不使用特征检测。您需要检测的唯一功能是 android 当前的怪癖。如果您检测到具有触摸事件的功能,那么它将禁用触摸屏笔记本电脑的鼠标。 e.preventDefault() 在 android 上失败。 触摸和鼠标的设备怎么样? 这也不能正常工作。我已经用类范围和没有范围进行了测试,那么每种方式都有自己的问题。无论您如何尝试使用touchstartmousedown 触发函数,在类范围内使用此代码只会触发mousedown。如果没有范围,它会一直触发您第一次触发的一个结果。【参考方案6】:

我建议你试试jquery-fast-click。我在这个问题上尝试了另一种方法和others。每个人都解决了一个问题,并引入了另一个问题。快速点击首次在 Android、ios、桌面和桌面触摸浏览器上运行(呻吟)。

【讨论】:

【参考方案7】:

编写此代码并添加j查询punch touch js。它将使用触摸事件模拟鼠标事件

function touchHandler(event)

    var touches = event.changedTouches,
        first = touches[0],
        type = "";
         switch(event.type)
    
        case "touchstart": type = "mousedown"; break;
        case "touchmove":  type="mousemove"; break;        
        case "touchend":   type="mouseup"; break;
        default: return;
    

    var simulatedEvent = document.createEvent("MouseEvent");
    simulatedEvent.initMouseEvent(type, true, true, window, 1, 
                              first.screenX, first.screenY, 
                              first.clientX, first.clientY, false, 
                              false, false, false, 0/*left*/, null);
    first.target.dispatchEvent(simulatedEvent);
    event.preventDefault();


function init() 

    document.addEventListener("touchstart", touchHandler, true);
    document.addEventListener("touchmove", touchHandler, true);
    document.addEventListener("touchend", touchHandler, true);
    document.addEventListener("touchcancel", touchHandler, true);    
 

【讨论】:

【参考方案8】:

这个原生解决方案最适合我:

    touchstart 事件添加到全局touch = true 的文档设置中。 在 mousedown/touchstart 处理程序中,在检测到触摸屏时阻止所有 mousedown 事件:if (touch && e.type === 'mousedown') return;

【讨论】:

【参考方案9】:

这是一个非常古老的问题,但我遇到了同样的问题,并找到了另一个解决方案,它不stopPropagation()preventDefault() 或嗅探设备类型。我在这个解决方案上工作的假设是设备同时支持触摸和鼠标输入。

说明:当触发触摸时,事件顺序为1)touchstart2)touchmove3)touchend4)mousemove5)mousedown6)mouseup7)@987654329 @。基于此,我们将标记从touchstart(链中第一个)到click(链中最后一个)的触摸交互。如果mousedown 在此触摸交互之外注册,则可以安全地拾取。

下面是 Dart 中的逻辑,在 js 中应该是非常可复制的。

var touchStarted = false;
document.onMouseDown.listen((evt) 
  if (!touchStarted) processInput(evt);
);
document.onClick.listen((evt) 
  touchStarted = false;
);
document.onTouchStart.listen((evt) 
  touchStarted = true;
  processInput(evt);
);

如您所见,我的听众位于document。因此,重要的是我不要stopPropagation()preventDefault() 这些事件,以便它们可以冒泡到其他元素。这个解决方案帮助我挑选出一个交互来采取行动,希望它也能帮助你!

【讨论】:

【参考方案10】:

我认为最好的方法是:

var hasTouchStartEvent = 'ontouchstart' in document.createElement( 'div' );

document.addEventListener( hasTouchStartEvent ? 'touchstart' : 'mousedown', function( e ) 
    console.log( e.touches ? 'TouchEvent' : 'MouseEvent' );
, false );

【讨论】:

那么我的鼠标在我的触摸屏笔记本电脑上就不能工作了。如果你为 mousedown 添加另一个事件,android 将在触摸时触发。 大多数新笔记本电脑也支持触摸屏,所以会有这些事件..不要这样做!

以上是关于如何绑定 Mousedown 和 Touchstart,但不响应两者?安卓、jQuery的主要内容,如果未能解决你的问题,请参考以下文章

WPF如何将mousedown(命令/动作)绑定到标签

解决如何在点击按钮时,不触发input的失去焦点事件

mousedown/mouseup/click执行顺序

粗浅的总结下事件流

如何获取项目绑定的集合的名称?

如何区分 mousedown 和 mousedrag