【问题标题】:JavaScript touchend versus click dilemmaJavaScript touchend 与点击的困境
【发布时间】:2014-10-23 16:32:45
【问题描述】:

我正在开发一些 javascript UI,并使用大量触摸事件(如“touchend”)来改进触摸设备的响应。但是,有一些逻辑问题困扰着我......

我看到许多开发人员在同一个事件中混合使用“touchend”和“click”。在许多情况下它不会受到伤害,但本质上该函数会在触摸设备上触发两次:

button.on('click touchend', function(event) {
  // this fires twice on touch devices
});

有人建议可以检测触摸功能,并适当地设置事件,例如:

var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click';
button.on(myEvent, function(event) {
  // this fires only once regardless of device
});

上面的问题是,它会在同时支持触摸和鼠标的设备上中断。如果用户当前在双输入设备上使用鼠标,则不会触发“单击”,因为仅将“touchend”分配给按钮。

另一种解决方案是检测设备(例如“iOS”)并基于此分配事件: Click event called twice on touchend in iPad。 当然,上面链接中的解决方案仅适用于 iOS(不是 Android 或其他设备),看起来更像是解决一些非常基本的问题的“黑客”。

另一种解决方案是检测鼠标运动,并将其与触摸功能结合起来,以确定用户是使用鼠标还是触摸。问题当然是用户可能不会在您想要检测鼠标时移动鼠标......

我能想到的最可靠的解决方案是使用一个简单的debounce 函数来简单地确保该函数仅在很短的时间间隔内(例如 100 毫秒)触发一次:

button.on('click touchend', $.debounce(100, function(event) {
  // this fires only once on all devices
}));

我是否遗漏了什么,或者有人有更好的建议吗?

编辑:我在我的帖子之后找到了这个链接,它提出了与上述类似的解决方案: How to bind 'touchstart' and 'click' events but not respond to both?

【问题讨论】:

  • 这解决不了任何问题...... Modernizr 只是检测到“触摸”。该事件仍将在触摸设备上触发两次。或者,如果您为触摸设备(通过modernizr)分配“触摸”事件,它将停止在像许多windows8设备这样的双输入设备上使用鼠标的用户。

标签: javascript jquery ios click touch


【解决方案1】:

经过一天的研究,我认为最好的解决方案是坚持单击并使用https://github.com/ftlabs/fastclick 来消除触摸延迟。我不是 100% 确定这与 touchend 一样有效,但至少相差不远。

我确实想出了一种方法,通过使用 stopPropagationpreventDefault 来禁用两次触摸触发事件,但这很狡猾,因为它可能会干扰其他触摸手势,具体取决于应用它的元素:

button.on('touchend click', function(event) {
  event.stopPropagation();
  event.preventDefault();
  // this fires once on all devices
});

事实上,我一直在寻找一种在某些 UI 元素上结合 touchstart 的解决方案,但除了解决方案之外,我看不到如何将其与 click 结合使用以上。

【讨论】:

  • 是否有本地方法来检测浏览器是否支持 touchstart/end?
  • 在没有 e.stopPropagation() 的父包装器的情况下,你是如何让它在同一个按钮元素上工作的?我已经尝试通过 angular 与 e.stopPropagation() 附加(点击)和(touchend)在他们都附加到的方法上,它仍然会触发两次。
【解决方案2】:

此问题已回答,但可能需要更新。

根据a notice from Google,如果我们在<head>元素中包含以下行,将不会再有300-350ms的延迟。

<meta name="viewport" content="width=device-width">

就是这样!并且点击和触摸事件不再有区别!

【讨论】:

  • 这很有趣。也许是时候摆脱 fastclick.js 了……我将在移动浏览器中对其进行一些测试。
  • 元标签应该放在head标签中,而不是header标签中。
  • 这对我来说不是真的。
  • 没有 300 毫秒的延迟如何解决不需要点击和触摸结束事件的问题?我听说有些安卓设备在点击事件时不会触发。
【解决方案3】:

是的,禁用双击缩放(以及因此的点击延迟)通常是最好的选择。我们终于有了很好的建议,那就是soon work on all browsers

如果出于某种原因,您不想这样做。您还可以使用UIEvent.sourceCapabilities.firesTouchEvents 显式忽略多余的clickpolyfill for this 的作用类似于您的去抖动代码。

【讨论】:

  • polyfill 链接现已消失
【解决方案4】:

您好,您可以通过以下方式实现。

function eventHandler(event, selector) {
    event.stopPropagation(); // Stop event bubbling.
    event.preventDefault(); // Prevent default behaviour
    if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks.
}

// Implement
$('.class').on('touchend click', function(event) {
    eventHandler(event, $(this)); // Handle the event.
    // Do somethings...
});

【讨论】:

    【解决方案5】:

    您的 debounce 函数会将每次点击的处理延迟 100 毫秒:

    button.on('click touchend', $.debounce(100, function(event) {
      // this is delayed a minimum of 100 ms
    }));
    

    相反,我创建了一个cancelDuplicates 函数,该函数会立即触发,但在 10 毫秒内的任何后续调用都将被取消:

    function cancelDuplicates(fn, threshhold, scope) {
        if (typeof threshhold !== 'number') threshhold = 10;
        var last = 0;
    
        return function () {
            var now = +new Date;
    
            if (now >= last + threshhold) {
                last = now;
                fn.apply(scope || this, arguments);
            }
        };
    }
    

    用法:

    button.on('click touchend', cancelDuplicates(function(event) {
      // This fires right away, and calls within 10 ms after are cancelled.
    }));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多