209

我正在开发一个必须在各种设备上运行的移动网站。目前让我头疼的是黑莓。

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

理想情况下,我会使用:

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

但我们遇到的问题是,其中一些黑莓设备从触摸到触发点击之间存在非常烦人的延迟。

补救措施是改用 touchstart:

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

但是我如何去绑定两个事件,但只触发一个?我仍然需要键盘设备的点击事件,但当然,如果我使用的是触摸设备,我不希望触发点击事件。

一个额外的问题:有没有办法做到这一点,并额外适应甚至没有触摸启动事件的浏览器?在对此进行研究时,看起来 BlackBerry OS5 不支持 touchstart,因此还需要依赖该浏览器的点击事件。

附录:

也许更全面的问题是:

使用 jQuery,是否可以/推荐使用相同的绑定处理触摸交互和鼠标交互?

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

  1. 我们使用 WURFL 来获取设备信息,因此可以创建我们自己的设备矩阵。根据设备,我们将使用 touchstart 或 click。

  2. 通过 JS 检测浏览器中的触摸支持(我需要对此进行更多研究,但似乎这是可行的)。

但是,这仍然存在一个问题:支持 BOTH 的设备怎么样。我们支持的一些手机(即诺基亚和黑莓)既有触摸屏又有键盘。所以那种让我回到原来的问题……有没有办法以某种方式同时允许两者?

4

37 回答 37

143

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


绑定到两者,但制作一个标志,以便该函数仅每 100 毫秒左右触发一次。

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

  return false
});
于 2011-08-11T00:14:42.350 回答
73

这是我“创建”的修复程序,它取出了 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

});
于 2012-07-16T15:33:03.663 回答
48

你可以尝试这样的事情:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
于 2011-09-23T14:21:26.680 回答
42

通常这也有效:

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

});
于 2012-11-03T22:28:22.803 回答
26

只需在事件函数return false;末尾添加即可解决此问题。on("click touchstart")

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

来自.on()的 jQuery 文档

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

于 2016-04-06T13:20:14.060 回答
11

我不得不做类似的事情。这是对我有用的简化版本。如果检测到触摸事件,则移除点击绑定。

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

在我的例子中,click 事件被绑定到一个<a>元素,所以我必须删除 click 绑定并重新绑定一个 click 事件,这会阻止<a>元素的默认操作。

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});
于 2013-12-18T12:07:03.737 回答
8

我通过以下方式成功了。

十分简单...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});
于 2012-04-18T14:01:31.867 回答
6

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

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

触摸端

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

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


mouseup

当鼠标指针悬停在元素上并释放鼠标按钮时,将向元素发送 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>


编辑 (2017)

截至 2017 年,从 Chrome 开始的浏览器正在采取措施,.on("click")通过消除点击请求上的点击事件产生的延迟,使点击事件对鼠标和触摸更加兼容。

这导致得出的结论是,恢复为仅使用单击事件将是最简单的解决方案。

我还没有做任何跨浏览器测试,看看这是否实用。

于 2017-04-21T08:09:40.227 回答
3

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

如果您在 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
     }
}
于 2011-08-11T00:46:03.877 回答
3

检查来自谷歌https://developers.google.com/mobile/articles/fast_buttons的快速按钮和 chost 点击

于 2012-07-20T09:50:54.223 回答
3

嗯...所有这些都超级复杂。

如果你有modernizr,那就不费吹灰之力了。

ev = Modernizr.touch ? 'touchstart' : 'click';

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});
于 2014-05-16T04:26:55.423 回答
2

另一种实现更好的维护。但是,这种技术也会执行 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()) {
      ...
   }
}
于 2012-04-28T14:33:17.230 回答
2

仅出于文档目的,这是我为最快/响应最快的桌面点击/点击移动解决方案所做的,我能想到:

on我用一个修改过的函数替换了 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

于 2013-04-25T17:28:17.613 回答
2

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

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

于 2014-12-15T15:48:10.177 回答
2

你可以这样尝试:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
于 2015-08-20T02:34:30.063 回答
2

就我而言,这非常有效:

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

主要问题是当我尝试点击鼠标时,在触摸设备上同时触发点击和触摸结束,如果我使用点击关闭,某些功能在移动设备上根本不起作用。click 的问题是这是一个全局事件,它会触发包括 touchend 在内的其余事件。

于 2017-10-02T14:21:05.577 回答
1

对我来说,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;
});
于 2014-08-05T07:08:05.790 回答
1

这对我有用,移动设备会同时监听两者,因此请防止一个,即触摸事件。桌面只听鼠标。

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });
于 2015-12-04T09:57:36.767 回答
1

此处未提及,但您可能需要查看此链接:https ://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the-same-element/

回顾一下后代,而不是尝试分配给两个处理程序然后整理结果,您可以简单地检查设备是否是触摸屏并仅分配给相关事件。观察:

var clickEvent = (function() {
  if ('ontouchstart' in document.documentElement === true)
    return 'touchstart';
  else
    return 'click';
})();

// and assign thusly:

el.addEventListener( clickEvent, function( e ){ 
    // things and stuff
});

我正在使用它来绑定我的事件,以便我可以在处理这两个事件的触摸屏上进行测试,以及在我的开发 PC 上只touchstart听到clickclick

不过,该链接的作者提到的一个问题是触摸屏笔记本电脑旨在处理这两种事件:

我了解到了我没有考虑的第三个设备,触摸屏笔记本电脑。它是一种支持触摸和点击事件的混合设备。绑定一个事件意味着仅支持该事件。这是否意味着拥有触摸屏和鼠标的人必须明确触摸,因为这是我正在处理的唯一事件?

绑定似乎是处理这些混合设备的理想选择touchstartclick为了防止事件触发两次,我在回调函数中添加了e.stopPropagation()和。阻止事件“冒泡”到他们的父母,但也阻止第二个事件触发。我将其列为“以防万一”,但似乎可以省略。e.preventDefault()e.stopPropagation()e.preventDefault()

于 2018-09-23T23:38:31.583 回答
0

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

最后,你可以 binb .live("touchstart", function(e) { e.stopPropagation(); }); 要求 touchstart 事件停止传播,客厅到 click() 被触发。

它对我有用。

于 2012-03-27T04:12:50.063 回答
0

在尝试解决这个问题时,有很多事情需要考虑。大多数解决方案要么中断滚动,要么不能正确处理重影点击事件。

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

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

于 2012-07-18T14:29:40.410 回答
0

分配给事件'touchstart mousedown''touchend mouseup'避免使用click.

于 2012-11-16T23:50:00.513 回答
0

利用点击将始终跟随触摸事件这一事实,这是我为摆脱“幽灵点击”而无需使用超时或全局标志所做的。

$('#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事件在元素上触发时,一个标志集,然后在点击时被删除(并被忽略)。

于 2013-02-08T21:11:52.143 回答
0

为什么不使用 jQuery 事件 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
};
于 2013-02-23T14:46:05.930 回答
0

如果您使用的是 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));

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

可能会帮助遇到类似情况的人!

于 2014-02-21T02:46:59.610 回答
0

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

var element = $("#element");

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

如果定义了 touchstart 事件,则返回“touch”,否则返回“no touch”。就像我说的那样,这是一种简单的点击/点击事件方法。

于 2014-03-22T05:20:23.280 回答
0

我正在尝试这个,到目前为止它有效(但我只在 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”的处理程序,所以有一个简单的函数可以让我在没有太多代码的情况下处理问题,这就是我这样做的原因。

于 2014-04-01T22:22:55.003 回答
0

编辑:我以前的答案(基于此线程中的答案)不是我要走的路。我想要一个子菜单在鼠标输入或触摸单击时展开,并在鼠标离开或其他触摸单击时折叠。由于鼠标事件通常在触摸事件之后触发,因此编写同时支持触摸屏和鼠标输入的事件侦听器有点棘手。

jQuery插件:触摸或鼠标

我最终编写了一个名为“ Touch Or Mouse ”(缩小 897 字节)的 jQuery 插件,它可以检测事件是由触摸屏还是鼠标调用(无需测试触摸支持!)。这样可以同时支持触摸屏和鼠标并完全分离它们的事件。

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

示范

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

$(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. 这个插件需要在ie上调用。要跟踪的主体元素touchstarttouchend事件,这样touchend事件就不必在触发元素(即链接或按钮)上触发。在这两个触摸事件之间,该插件认为任何鼠标事件都会被触摸调用。
  2. 鼠标事件仅在 之后触发touchend,当鼠标事件在ghostEventDelay(选项,默认为 1000 毫秒)之后触发时touchend,此插件认为鼠标事件是通过触摸调用的。
  3. 当使用触摸屏点击一个元素时,该元素会获得:active状态。该mouseleave事件仅在元素通过 ie 失去此状态后才会触发。单击另一个元素。因为这可能是mouseenter事件被触发后的几秒(或几分钟!),所以这个插件会跟踪元素的最后一个mouseenter事件:如果最后一个mouseenter事件是由触摸调用的,则以下mouseleave事件也被认为是由触摸调用的。
于 2014-10-01T15:50:16.647 回答
0

尝试使用Virtual Mouse (vmouse) Bindingsfrom jQuery Mobile。这是虚拟事件,特别适合您的情况:

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

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

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

于 2015-02-16T12:59:22.743 回答
0

这是一个简单的方法:

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

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

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

这是一个你可以玩的代码笔(尝试点击触摸设备上的按钮——应该没有点击延迟):http ://codepen.io/thdoan/pen/xVVrOZ

我还将它实现为一个简单的 jQuery 插件,它还支持通过传递选择器字符串来过滤 jQuery 的后代:

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

代码笔: http ://codepen.io/thdoan/pen/GZrBdo/

于 2016-03-09T08:09:27.563 回答
0

UDPATE:

我一直在研究一种实现,将 click 和 touchend 事件用于同一个函数,如果类型发生变化,该函数会有效地阻止事件。我的目标是拥有一个响应速度更快的应用程序界面——我想减少事件开始到 UI 反馈循环的时间。

为了使这个实现工作,假设您在“点击”和“触摸结束”上添加了所有相关事件。如果两个事件都需要运行但属于不同类型,这可以防止一个元素被剥夺事件冒泡。

这是一个基于轻量级 API 的实现,我出于演示目的对其进行了简化。它演示了如何在折叠元素上使用该功能。

var tv = {
    /**
     * @method eventValidator()
     * @desc responsible for validating event of the same type.
     * @param {Object} e - event object
     * @param {Object} element - element event cache
     * @param {Function} callback - callback to invoke for events of the same type origin
     * @param {Object} [context] - context to pass to callback function
     * @param {Array} [args] - arguments array to pass in with context. Requires context to be passed
     * @return {Object} - new event cache
     */
    eventValidator: function(e, element, callback, context, args){
        if(element && element.type && element.type !== e.type){
            e.stopPropagation();
            e.preventDefault();
            return tv.createEventCacheObj({}, true);
        } else {
            element = tv.createEventCacheObj(e);
            (typeof context === "object" ? callback.apply(context, args) : callback());
            return element;
        }
    },

    /**
     * @method createEventCacheObj()
     * @param {Object} event - event object
     * @param {String} [event.type] - event type
     * @param {Number} [event.timeStamp] - time of event in MS since load
     * @param {Boolean} [reset=false] - flag to reset the object
     * @returns {{type: *, time: string}}
     */
    createEventCacheObj: function (event, reset){
        if(typeof reset !== 'boolean') reset = false;
        return {
            type: !reset ? event.type : null,
            time: !reset ? (event.timeStamp).toFixed(2): null
        };
    }
};

// Here is where the magic happens
var eventCache = [];
var pos = 0;

var $collapses = document.getElementsByClassName('tv-collapse__heading');
    Array.prototype.forEach.call($collapses, function(ele){
        ele.addEventListener('click', toggleCollapse);
        ele.addEventListener('touchend', toggleCollapse);

        // Cache mechanism
        ele.setAttribute('data-event-cache', String(pos++));
    });

/**
 * @func toggleCollapse()
 * @param {Object} e - event object
 * @desc responsible for toggling the state of a collapse element
 */
function toggleCollapse(e){
    eventCache[pos] = tv.eventValidator(e, eventCache[pos], function(){
       // Any event which isn't blocked will run the callback and its content
       // the context and arguments of the anonymous function match the event function context and arguments (assuming they are passed using the last two parameters of tv.eventValidator)

    }, this, arguments);
}

原答案:

这是对Rafael Fragoso 的回答的修改 - 纯 JS。

(function(){
  
  button = document.getElementById('sayHi');
  
  button.addEventListener('touchstart', ohHai);
  button.addEventListener('click', ohHai);

  function ohHai(event){
    event.stopPropagation();
    event.preventDefault();
    console.log('ohHai is:', event.type);
  };
})();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SO - Answer</title>
</head>
<body>
  <button id="sayHi">Anyone there?</button>
</body>
</html>

运行以下代码段并注意输出:

  • 电话
  • 药片
  • 平板电脑(桌面模式 - 如果适用)
  • 桌面
  • 桌面(触摸屏 - 如果适用)

关键是我们正在阻止连续的事件触发。当触摸发生时,移动浏览器会尽力模拟点击。我希望我能找到一篇文章的链接,该链接解释了在 touchstart 之后通过点击发生的所有事件。(我正在寻找双击和单击实际触发之间的 300 毫秒延迟)。

触摸和鼠标设备

我使用 Surface Pro 和带有触摸屏的 Windows 10 桌面进行了几次测试。我发现它们都触发了您所怀疑的事件,touchstart 用于触摸,click 用于触控板、鼠标和造型师。有趣的是,靠近但不在按钮上的触摸事件会在没有触摸事件的情况下触发点击事件。似乎 Windows 10 中的内置功能会查找半径内最近的节点,如果找到节点,它将触发基于鼠标的事件。

同一类型的多个事件

如果一个元素上有两个相同类型的事件,阻止事件冒泡可能会阻止其中一个事件触发。有几种不同的方法可以使用某种缓存来处理这个问题。我最初的想法是修改事件对象,但我们得到了一个参考,所以我认为缓存解决方案就足够了。

于 2016-09-15T00:30:58.273 回答
0

我发现最好的方法是编写触摸事件并让该事件以编程方式调用正常的点击事件。这样您就拥有了所有正常的点击事件,然后您只需为所有触摸事件添加一个事件处理程序。对于您想要使其可触摸的每个节点,只需将“可触摸”类添加到它以调用触摸处理程序。使用 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
});
于 2016-12-06T04:38:51.113 回答
0

查找文档滚动移动差异(水平和垂直) touchstart 和 touchend ,如果其中一个大于 1 像素,则它是移动而不是单击

var touchstartverscrollpos , touchstarthorscrollpos;


    $('body').on('touchstart','.thumbnail',function(e){

        touchstartverscrollpos = $(document).scrollTop();
        touchstarthorscrollpos = $(document).scrollLeft();


    });



    $('body').on('touchend','.thumbnail',function(e){


        var touchendverscrollpos = $(document).scrollTop();
        var touchendhorscrollpos = $(document).scrollLeft();

        var verdiff = touchendverscrollpos - touchstartverscrollpos;
        var hordiff = touchendhorscrollpos - touchstarthorscrollpos;


        if (Math.abs(verdiff) <1 && Math.abs(hordiff)<1){

// do you own function () here 



            e.stopImmediatePropagation();

            return false;
        }

    });
于 2017-01-25T04:20:54.650 回答
0

我在那里给出了答案,并用jsfiddle进行了演示。您可以检查不同的设备并进行报告。

基本上我使用一种事件锁和一些服务它的功能:

/*
 * Event lock functions
 * ====================
 */
function getEventLock(evt, key){
   if(typeof(eventLock[key]) == 'undefined'){
      eventLock[key] = {};
      eventLock[key].primary = evt.type;
      return true;
   }
   if(evt.type == eventLock[key].primary)
      return true;
   else
      return false;
}

function primaryEventLock(evt, key){
   eventLock[key].primary = evt.type;
}

然后,在我的事件处理程序中,我首先请求我的锁:

/*
 * Event handlers
 * ==============
 */
$("#add").on("touchstart mousedown", addStart);
$("#add").on("touchend mouseup", addEnd);
function addStart(evt){
   // race condition between 'mousedown' and 'touchstart'
   if(!getEventLock(evt, 'add'))
      return;

   // some logic
   now = new Date().getTime();
   press = -defaults.pressDelay;
   task();

   // enable event lock and(?) event repetition
   pids.add = setTimeout(closure, defaults.pressDelay);

   function closure(){
        // some logic(?): comment out to disable repetition
      task();

      // set primary input device
      primaryEventLock(evt, 'add');

      // enable event repetition
      pids.add = setTimeout(closure, defaults.pressDelay);
   }
}
function addEnd(evt){
      clearTimeout(pids.add);
}

我必须强调,问题不在于仅仅对事件做出回应,而是对两者都没有回应。

最后,在jsfiddle有一个更新版本的链接,我通过在事件开始和结束处理程序以及 2 个范围变量eventLockeventLockDelay.

于 2018-06-25T14:45:10.380 回答
0

如果您已经在使用 jQuery,那么有两种方法可以处理这个问题,它们都非常简短。我认为它不需要像大多数答案那样复杂......

  1. 返回假;

只需监听任何/所有事件并添加“return false;” 最后停止额外的重复事件处理。

thing$.on('click touchend',function(){
    // do Stuff
    return false; // stop
});

可重用示例:

function bindClickAndTouchEvent(__targets$, __eventHandler){
    
    __targets$.on('click touchend',function(){
        __eventHandler.apply(this,arguments);// send original event
        return false;// but stop additional events
    });
}

// In-Line Usage:

bindClickAndTouchEvents( $('#some-element-id'), function(){ 
    console.log('Hey look only one click even when using touch on a touchscreen laptop') 
});
  1. 鼠标向上和触摸结束

根据您的用例,您可能只使用 mouseup 和 touchend,因为这两个事件根本不重叠,并且只创建一个事件开始......那么您甚至不需要“return false;”。

thing$.on('mouseup touchend',function(){
    // do Stuff
});
于 2021-10-07T19:33:37.317 回答
-1

我不确定这是否适用于所有浏览器和设备。我使用 Google Chrome 和 Safari iOS 对此进行了测试。

$thing.on('click || touchend', function(e){

});

OR 操作数应该只触发第一个事件(在应该点击的桌面和应该是 touchend 的 iPhone 上)。

于 2015-10-27T14:19:35.850 回答
-2

您可以使用计数器代替超时:

var count = 0;
$thing.bind('touchstart click', function(){
  count++;
  if (count %2 == 0) { //count 2% gives the remaining counts when devided by 2
    // do something
  }

  return false
});
于 2012-11-09T16:29:50.557 回答