当绑定'touchstart'和'click'时,如何防止幽灵点击?

16

我正在开发一个应用程序,在我的台式机和手机上进行测试。我正试图修复手机上缓慢的按钮按下问题,并且刚学习到touchstart和click之间的差异。

因此,我使用jquery绑定了我的按钮,执行了touchstart和click,代码如下:

$('.button').on('touchstart click', function(){
});
现在该应用在手机上的表现稍微好了一些。但它会执行一次轻触点击,然后是一次常规点击,也就是潜在的双击,或者在下一张幻灯片进入时,它会点击那里的某些内容。在我的情况下,一个表单出现了,并且选中了在按钮所在位置出现的输入字段。这是幽灵点击。
我该如何告诉我的函数,如果有轻触,就忽略点击呢?
或者更好的方法是,是否有一种方法可以告诉jquery将所有“点击”都视为轻触?
我会删除“点击”绑定,但这样我就不能在我的桌面环境中轻松测试了。
9个回答

23
如果您想针对不同的事件类型执行特定操作,请使用e.type
$('.button').on('touchstart click', function(e) {    
    e.preventDefault(); //prevent default behavior
    if(e.type == "touchstart") {
        // Handle touchstart event.
    } else if(e.type == "click") {
        // Handle click event.
    }
});

@willdanceforfun - 很高兴能帮忙。 - Venkata Krishna
7
这并不会阻止在Firefox中触发单击事件(目前我只测试了这一个浏览器)。它只是同时执行两个操作,如果你希望在移动设备上与桌面浏览器的行为相同,它仍然会执行该操作两次。 - Sean Kendle
1
请注意-此解决方案在三星浏览器上不起作用(因此对于操作系统版本低于4.4的设备中的Webview,它将触发两个事件)。 - Yaniv Efraim
4
e.stopPropagation() 无法阻止第二个事件的触发。不过,e.preventDefault() 可以解决这个问题。 - Jonathan Lidbeck
e.stopPropagation() 如何停止第二个事件?它以及 e.preventDefault() 只对当前事件起作用,这里我们将有两个独立的事件。 - Georgii Ivankin

12

如果事件类型是触摸事件(touchstart),那么在单击事件(click)之前应该先触发touchstart。因此,我会在触摸事件触发时删除单击事件的处理程序。这种方法似乎非常适合我的需求:

$('.do-popdown').on('click touchstart', handlePopdown);
function handlePopdown(e){
    if(e.type == 'touchstart') {
        $('.do-popdown').off('click', handlePopdown).click(function(e){
            // do nothing
            e.stopPropagation();
            e.preventDefault();
            return false;
        });
    }
    // do my handling...
    // and nothing else:
    e.stopPropagation();
    e.preventDefault();
    return false;
}

e.stopPropagation(); e.preventDefault(); return false; 可能有点过度,但我在各种不同的元素(<a><div><span><button> 等)上使用它,它满足了我的通常需求。

编辑:一个更通用(更好?)的方法是创建类似于熟悉的 .click() 事件绑定器:

jQuery.fn.extend({
    clickOrTouch: function(handler) {
        return this.each(function() {
            var event = ('ontouchstart' in document) ? 'touchstart' : 'click';
            $(this).on(event, handler);
        });
    }
});

这样,只有适当的事件附加到所选元素。

我想一个更“合适”的名称应该是.touchstartOrClick(),但由于它在某种程度上是.click()的替代,我决定以click开头命名。

这可以像.click()一样使用。例如,在div中放置一个button并运行:

$('button').clickOrTouch(doSomething);
$('div').clickOrTouch(doSomething);
function doSomething(e) {
    $('body').append(this.tagName + ' ' + e.type + '<br>');
    e.stopPropagation();
}

这将根据触发方式显示“DIV click”或“BUTTON touchstart”。如果在链接上使用,请在处理程序中添加e.preventDefault()(如果需要)。


乍一看,看起来不错!我得试试这个。实际上,我有点放弃了我原来的路线,但毫无疑问,我会回来的。 - Sean Kendle
3
请注意,在具有鼠标和触摸事件的设备上,仅检测设备上的触摸事件是不够的,因为解绑由于触摸而禁用鼠标点击事件。 - Dtipson
2
你不应该为 touchstartclick 附加处理程序,因为某些设备会同时具有两者:一个用于触摸屏幕时,另一个用于使用鼠标/触控板时。 - Georgii Ivankin

4
这段代码来自@Krishna,但对我无效:
$('.button').on('touchstart click', function(e) {    
    e.stopPropagation(); //stops propagation
    if(e.type == "touchstart") {
        // Handle touchstart event.
    } else if(e.type == "click") {
        // Handle click event.
    }
});

但是这个应该可以工作,就像@Slashback最初建议的那样。
$('.button').on('touchstart click', function(e) {    
    e.preventDefault();

    if(e.type == "touchstart") {
        // Handle touchstart event.
    } else if(e.type == "click") {
        // Handle click event.
    }
});

.preventDefault()的单独使用对我有效,而不必使用.stopPropagation()。 我也不需要做太多额外的工作。


2
这似乎更像是一条评论而不是一个答案。 - user4275591

1
我正在使用PhoneGap。这可能会有帮助:

function longTapEvent(id, longTapFun) {
    $(id).click(function() {
        longTapFun();
        return;
    })

    $(id).unbind("touchstart");
    $(id).bind("touchstart", function() {
        //do sth...
    });
    $(id).unbind("touchend");
    $(id).bind("touchend", function() {
        //do sth...
    });
}

1
您可以检测点击事件并在前一次触发不是点击(即是触摸或其他事件)时退出当前函数执行 - 在原生JavaScript中:
buttonx.addEventListener('touchend',clicktap);
buttonx.addEventListener('click',clicktap);

function clicktap(e) {
var c = e.type.includes("click"); // or "mouse" if using "mousedown"
if (c && (this.wastouched)) return this.wastouched = 0;
this.wastouched = !c;

// ..run things..

}

0

这是我自己研发的解决方案,请尽情批评!谢谢!

基本上,如果您点击价格按钮,该商品将立即添加到购物车中。

如果您轻触/按下按钮,则会显示价格。 第二次轻触/按下它,商品将被添加到购物车中。

在此处查看实际效果:https://www.indospace.io/studio-city/urban-treez

    var price_button = $(evt.target).closest('.price-button')
    if(price_button.length) {
        price_button.on('mouseleave', function() {
            $(this).removeAttr('data-touched')
        })
        if(evt.type == 'touchstart') {
            jQuery.touch_start = true
            if(!price_button.attr('data-touched')) {
                price_button.attr('data-touched', 1)
                return
            }
            price_button.attr('data-touched', 2)
        }
        if(evt.type == 'click') {
            if(price_button.attr('data-touched'))
                return
        }
        bag.add(price_button)
        return true
    }

0
如果目的是区分用户拖动和点击,则使用原生JS中的touchmove而不是touchstart对我来说很有效:
 document.querySelector("#mydiv").addEventListener("click", function(e){
    console.log("click");
 });
 let count = 0;
 document.querySelector("#mydiv").addEventListener("touchmove", function(e){
    e.preventDefault();

    // if need to detect once
    //count++;
    //if(count == 3){
    //  console.log("dragged!");  // dragHandler(), and reset count later
    //}

    console.log("touchmove");
 });

更多信息:https://developer.mozilla.org/zh-CN/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent


-1

-2

我在我的项目中使用以下内容:

$('.button').on('touchstart', function() {

   var thisButton = $(this); 
   if(!thisButton.hasClass('disabled')) {
     thisButton.addClass('disabled'); //add class disabled to prevent double execution

     ............rest of code

     window.setTimeout(function () {
        thisButton.removeClass('disabled'); //remove class disalbed after 300ms
     }, 300);

});

我希望这能对某人有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接