使用Javascript防止来自Mobile Safari / iPhone中的触摸事件的鼠标仿真事件(即单击)

时间:2010-05-23 06:58:48

标签: javascript-events iphone

在使用交互式DOM元素的单页Javascript应用程序中,我发现“mouseover-mousemove-mousedown-mouseup-click”序列发生在之后touchstart-touchmove-touchend”序列中事件。

我还发现,在 mouse*-click 事件期间执行“event.preventDefault()”可以防止“touchstart”事件发生,但,而不是touchmovetouchend期间。这是一个奇怪的设计,因为在touchstart期间无法知道用户是想要拖动还是滑动还是只是点击/点击该项目。

我最终设置了一个与时间戳相关的“ignore_next_click”标志,但这显然不是很干净。

有没有人知道更好的方法,或者我们错过了什么?

请注意,虽然“点击”可以被识别为“touchstart-touchend”序列(即没有“touchmove”),但是某些事情,例如键盘输入焦点,只能发生在适当的click事件中。

10 个答案:

答案 0 :(得分:4)

我遇到了制作跨平台HTML5 / JS应用程序的类似问题。对我来说唯一真正的答案是 preventDefault 触摸事件,并根据我的逻辑实际管理触摸状态并点击我自己的点击,拖动等事件。这听起来比实际上更令人生畏,但模仿的点击/鼠标事件在大多数移动浏览器上都能完美运行。

点击,额外的鼠标序列就是为了您的方便(和兼容性)。我的经验法则 - 如果它是为了您的方便,但它不方便,最好杀死它。

就输入框而言,它们只需要touchend事件。我已经杀死了点击/鼠标事件,仍然可以让移动浏览器正确响应输入的触摸。如果它仍然给你带来问题,你可以修改事件处理程序以仅抑制非输入事件:

function touchHandler(event) {
    var shouldIgnore = event.target != null 
          && ( event.target.tagName.toLowerCase() == "input" || event.target.tagName.toLowerCase() == "textarea" );

    if(!shouldIgnore) e.preventDefault();
}

答案 1 :(得分:3)

我自己已经找到了解决方案,因为我在其他地方找不到足够的解决方案:

   var isTouch = ('ontouchstart' in window);

   function kill(type){
     window.document.body.addEventListener(type, function(e){
       e.preventDefault();
       e.stopPropagation();
       return false;
     }, true);
   }

   if( isTouch ){
     kill('mousedown');
     kill('mouseup');
     kill('click');
     kill('mousemove');
   }

检查isTouch可让鼠标输入设备正常工作,但会杀死Safari / iOS上的模拟事件。诀窍是在useCapture = true的调用中使用addEventListener,这样我们就可以挖掘页面中的所有鼠标事件,而无需在整个Web应用程序中破解代码。请参阅此处的函数文档:https://developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener?redirectlocale=en-US&redirectslug=DOM%2Felement.addEventListener

编辑:

既然用于处理此问题的库更好,您可以使用类似Fastclick的替代方法(https://github.com/ftlabs/fastclick)。

答案 2 :(得分:2)

如果您必须支持同时支持鼠标和触摸的设备,另一种解决方案是使用捕获事件监听器来停止发生的所有鼠标事件

  • 触摸事件后的延迟
  • 与触摸事件处于同一位置
  • 与触摸事件相同的目标元素

触摸事件的信息(时间,位置或目标元素)可以记录在另一个捕获事件监听器中。

答案 3 :(得分:1)

在Window.matchesMedia函数中包装仅鼠标代码是我找到的最简洁的方法。

if (window.matchMedia('(hover: hover), (any-hover: hover), (-moz-touch-enabled: 0)').matches) {
    el.addEventListener('mouseover', ev => {
         // mouse handler, no simulated hover
    }
}

这可用于防止模拟悬停,但也可能会阻止模拟点击。

注意:从版本58开始,Firefox上需要-moz-touch-enabled部分。

答案 4 :(得分:1)

只需防止touchend事件。当您触摸元素时,它将允许浏览器滚动页面,但不会使其发出人为的鼠标事件。

element.addEventListener('touchend', event => {
  event.preventDefault();
});

答案 5 :(得分:0)

Creating Fast Buttons for Mobile Web Applications有解决问题的方法。

还要注意,当使用IE10时,preventDefault()不会在MSPointerDown事件之后停止ghost /合成/模拟鼠标事件,因此真正的跨浏览器解决方案更难。

答案 6 :(得分:0)

此解决方案使您可以侦听PointerEvents(如果存在),然后侦听TouchEvents(如果存在),然后侦听MouseEvents(如果另外两个都不存在)。移动Safari仍会同时提高touchstartmousedown,但您只会听touchstart

if (window.PointerEvent) {                                  /* decent browsers */
    etouch.addEventListener('pointerdown', (e) => {
        console.log('pointerdown');
    });
}
else if (window.TouchEvent) {                               /* mobile Safari */
    etouch.addEventListener('touchstart', (e) => {
        console.log('touchstart');
    });
}
else {                                                      /* desktop Safari */
    etouch.addEventListener('mousedown', (e) => {
        console.log('mousedown');
    });
}

答案 7 :(得分:0)

当设备支持触摸事件时,您可以尝试在“ click”,“ mousedown”或“ mouseup”事件上退出该功能。

use.addEventListener("click",function(e){

  // EXIT FUNCTION IF DEVICE SUPPORTS TOUCH EVENTS
  if ("ontouchstart" in document.documentElement) return false;

  // YOURMOUSEONLY CODE HERE

});

答案 8 :(得分:0)

在当前的浏览器上,使用'pointerwhatever'代替'mousewhatever'似乎可以正常使用(2019)。

即他们发明了way,其中所有的入门设备都具有相同的代码。

答案 9 :(得分:0)

向touchstart添加事件侦听器,该事件侦听器将数据接触的属性添加到元素。添加另一个事件侦听器以单击以检查是否涉及数据。如果存在,请阻止默认设置并将其删除。这是我实现中的一些JS。

var menuLinks = document.querySelectorAll('#my-nav>li>a');
for (var i = 0; i < menuLinks.length; i++) {
    var menuLink = menuLinks[i];
    menuLink.addEventListener('touchstart', function () {
        menuLink.setAttribute('data-touched', '');
});
menuLink.addEventListener('click', function (event) {
    if (menuLink.hasAttribute('data-touched')) {
        menuLink.removeAttribute('data-touched');
        event.preventDefault();
    }
});