繁体   English   中英

使用 Javascript 在 Mobile Safari / iPhone 中防止鼠标模拟事件(即点击)

[英]Preventing mouse emulation events (i.e. click) from touch events in Mobile Safari / iPhone using Javascript

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

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

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

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

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

只需防止touchend事件。 当您触摸元素时,它会让浏览器滚动页面,但不会让它发出人工鼠标事件。

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

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

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

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

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

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

我自己做了一个解决方案,因为我在其他地方没有找到足够的解决方案:

   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 上的模拟事件。 诀窍是在对addEventListener的调用中使用useCapture = true ,这样我们就可以获取页面中的所有鼠标事件,而无需破解整个网络应用程序的代码。 在此处查看该函数的文档: https : //developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener?redirectlocale= en-US &redirectslug=DOM%2Felement.addEventListener

编辑:

现在处理这个问题的库更好了,你可以使用像 Fastclick 这样的替代品( https://github.com/ftlabs/fastclick )。

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

  • 在触摸事件后的延迟内
  • 在与触摸事件相同的位置
  • 在与触摸事件相同的目标元素上

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

将鼠标专用代码包装在 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 部分。

此解决方案允许您侦听PointerEvents如果它们存在),然后是TouchEvents如果它们存在),然后是MouseEvents如果其他两个都不存在)。 Mobile 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');
    });
}

使用'pointerwhatever'而不是'mousewhatever'似乎在当前浏览器(2019)上运行良好。

即他们发明了一种对所有输入设备使用相同代码的方法

为移动 Web 应用程序创建快速按钮可以解决这个问题。

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

当设备支持触摸事件时,您可以尝试在“单击”、“鼠标按下”或“鼠标向上”事件上退出该功能。

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

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

  // YOURMOUSEONLY CODE HERE

});

将事件侦听器添加到 touchstart 以将属性 data-touched 添加到元素。 添加另一个事件侦听器以单击检查数据接触。 如果它在那里,请防止默认并将其删除。 这是我的实现中的一些 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();
    }
});

pointer...事件具有mouse...事件所缺少的pointerType类型属性。 您可以使用此属性来检测和忽略由触摸而不是鼠标生成的事件。

前:

window.addEventListner('mousemove', (e) => {
  /* No way to tell if this event came from a mouse or a finger */
  console.log(':(');
});

后:

window.addEventListner('pointermove', (e) => {
  if (e.pointerType !== 'mouse') return;
  /* This event definitely came from a mouse */
  console.log(':)');
});

您只需将mouse...事件侦听器替换为pointer...侦听器即可利用此属性。 pointer...事件在现代浏览器中得到很好的支持(至少可以追溯到三年前)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM