点击事件后jQuery调用点击事件

Posted

技术标签:

【中文标题】点击事件后jQuery调用点击事件【英文标题】:jQuery calling click event after taphold event 【发布时间】:2012-05-17 03:49:21 【问题描述】:

我正在使用 Jquery 和 Jquery Mobile 为 android 开发一个 PhoneGap 应用程序。

我有一个项目列表,需要将两个事件绑定到列表中的每个项目。我需要一个“taphold”事件和一个“click”事件。我遇到的问题是当我执行“taphold”时,会触发正确的“taphold”事件。但是,一旦我释放,点击事件也会被触发。如何防止点击事件在点击后触发?

代码:

function LoadMyItems(items) 

for(var idx in items)

    var itemLine = '<div class="my_item" id="my_item_'+items[idx].user_item_id+'">' +
           '<img class="item_icon_32" src=./images/graphicFiles/Icon48/'+items[idx].item.graphic.graphicFiles.Icon48.filename+' />' +
           items[idx].item.name+    
           '</div>';
    $('#my_list').append('<li>'+itemLine+'</li>');
        $('#my_item_'+items[idx].user_item_id).bind('taphold', userItem:items[idx],ShowMyItemInfo);
        $('#my_item_'+items[idx].user_item_id).bind('click tap', userItem:items[idx],FitMyUpgradeItem);
        console.log('UserItem '+items[idx].user_item_id+' loaded and events bound');
    
    $('#my_items_loader').hide();
    myScroll.refresh();

根据下面的建议,这就是我最终的结果。这适用于 iScroll 对象。

function LoadMyItems(items) 

for(var idx in items)

    var itemLine = '<div class="my_item" id="my_item_'+items[idx].user_item_id+'">' +
                   '<img class="item_icon_32" src=./images/graphicFiles/Icon48/'+items[idx].item.graphic.graphicFiles.Icon48.filename+' />' +
                   items[idx].item.name+    
                   '</div>';
    $('#my_list').append('<li>'+itemLine+'</li>');

    (function(index) 
        var tapTime = 0;
        var xPos = 0;
        var yPos = 0;
        $('#my_item_'+items[index].user_item_id).bind('vmousedown vmouseup', function (event) 
            if (event.type == 'vmousedown') 

                tapTime = new Date().getTime();
                xPos = event.pageX;
                yPos = event.pageY;

                var timer = setTimeout(function() 
                    var duration = (new Date().getTime() - tapTime);
                    var xDiff = Math.abs(mouseXPos - xPos);
                    var yDiff = Math.abs(mouseYPos - yPos);
                    if(duration >= 700 && (yDiff <= 40 || mouseXPos == 0))
                        ShowItemInfo(items[index].item);
                ,750);
             else 
                //event.type == 'vmouseup'
                var duration = (new Date().getTime() - tapTime);
                var xDiff = Math.abs(event.pageX - xPos);
                var yDiff = Math.abs(event.pageY - yPos);
                tapTime = new Date().getTime();
                if (duration < 699 && yDiff <= 40) 
                    //this is a tap
                    FitMyUpgradeItem(items[index]);
                
            
        );

        $('#my_item_'+items[index].user_item_id).bind('touchmove',function(event) 
            event.preventDefault();
        );
    )(idx);

    console.log('UserItem '+items[idx].user_item_id+' loaded and events bound');

$('#my_items_loader').hide();
myScroll.refresh();

【问题讨论】:

久经考验的快速解决方案:***.com/a/11941654/1386781 【参考方案1】:

而不是使用taptaphold(我尝试使用但遇到了同样的问题,这似乎是taphold 事件的固有问题)您可以使用vmousedown 并设置一个标志,然后绑定到vmouseup 以确定它是tap 还是taphold

var tapTime = 0;
$('#my_item_'+items[idx].user_item_id).bind('vmousedown vmouseup', function (event) 
    if (event.type == 'vmousedown') 
        tapTime = new Date().getTime();
     else 
        //event.type == 'vmouseup'
        //here you can check how long the `tap` was to determine what do do

        var duration = (new Date().getTime() - tapTime);
        if (duration > 3000) 
            //this is a tap-hold
            ShowMyItemInfo(items[idx]);
         else 
            //this is a tap
            FitMyUpgradeItem(items[idx]);
        
    
);

要使其正常工作,您必须在循环代码周围添加一个 IIFE 或更改 ShowMyItemInfo(items[idx]); 以在不引用更改循环每次迭代的变量的情况下工作。一个简单的创建 IIFE 就是使用$.each()。否则你的循环看起来像这样:

for(var idx in items)

    (function (idx) 
        ...
    )(idx);

IIFE = 立即调用函数表达式。它允许我们对传入 IIFE 的变量的当前状态进行“快照”。所以当我们传入idx(技术上第二个实例是传入的变量,第一个实例是IIFE中可用的变量,为简单起见可以更改为ids_new),值在tap 事件处理程序触发时保存传入的参数。

更新

您还可以设置超时来确定taphold,而不是使用vmouseup 事件:

//setup a timer and a flag variable
var tapTimer,
    isTapHold = false;
$('#my_item_'+items[idx].user_item_id).bind('vmousedown vmouseup', function (event) 
    if (event.type == 'vmousedown') 

        //set the timer to run the `taphold` function in three seconds
        //
        tapTimer = setTimeout(function () 
            isTapHold = true;
            ShowMyItemInfo(items[idx]);
        , 3000);
     else 
        //event.type == 'vmouseup'
        //clear the timeout if it hasn't yet occured
        clearTimeout(tapTimer);    

        //if the flag is set to false then this is a `tap` event
        if (!isTapHold) 
            //this is a tap, not a tap-hold
            FitMyUpgradeItem(items[idx]);
        

        //reset flag
        isTapHold = false;
    
);

这样,在用户按住手指三秒钟后,事件就会触发。然后tap 事件处理程序将仅在这三秒未发生时触发。

【讨论】:

好的,这很好用。太感谢了。我现在唯一的问题是如何使它成为真正的点击并按住,而不是点击、按住、释放。我希望它像 taphold 事件一样工作。所以在 ~750ms 后触发该函数以完成点击 @jasonlg3d 我添加了一个示例,说明如何使用超时来执行此操作,这样用户就不会抬起手指来获取反馈。请参阅我的答案的更新 你在的时候我正在研究这个。我在上面发布了我的解决方案。我必须做一些额外的事情来确保 iScroll 仍然滚动并且不会触发点击事件。 感谢您的完美解决方案。 我的 windows 8 手机只有一个问题。如果我“vmousedown”我的 div 并同时按下手指并移动它。然后当我松开手指时,不会触发“vmouseup”。这使得“ShowMyItemInfo”函数总是发生,因为 ClearTimeout 没有运行。【参考方案2】:

在您定义偶数之前,只需将其设置在文档顶部或任何位置:

$.event.special.tap.emitTapOnTaphold = false;

那么你可以这样使用它:

$('#button').on('tap',function()
    console.log('tap!');
).on('taphold',function()
    console.log('taphold!');
);

【讨论】:

【参考方案3】:

就我个人而言,我认为这里的答案过于复杂了。如果您只是想要一种简单的方法来继续使用 taphold 事件并忽略释放 taphold 时触发的 click 事件,以下是我在自己的项目中解决同样问题的方法:

// We will use this flag to ignore click events we don't want
var skipNextClick = false;

//Set up your event handler.  You could do these using two handlers or one.  I chose one.

$('div.element').on('click taphold', function (e) 
    //set up a quick bool flag that is true if click
    var isClick = (e.type == 'click');

    if (isClick && !skipNextClick) 
        //run your code for normal click events here...

    
    else if (isClick && skipNextClick) 
        //this is where skipped click events will end up...

        //we need to reset our skipNextClick flag here,
        //this way, our next click will not be ignored
        skipNextClick = false;
    
    else 
        //taphold event

        //to ignore the click event that fires when you release your taphold,
        //we set the skipNextClick flag to true here.
        skipNextClick = true;

        //run your code for taphold events here...

    
);

【讨论】:

如果您在释放之前移出元素,则仍然会触发 taphold 事件。如果你这样做,下一个点击事件将被跳过,因为当你移出元素时,点击事件正确地没有被触发。 我绝对可以看到会发生什么!我想有几种方法可以捕捉到这一点。也许寻找 touchstart 事件,注意它从哪个元素开始,然后寻找对应的 touchend 事件,看看它是否在原始元素或父元素之外。 ...或者 jQuery mobile 应该正确实现 tap/taphold 以便在释放之前移出元素时它不会触发 :)【参考方案4】:

改为使用 tap\vclick 和 taphold -- Tap 事件在点击后触发两次。

$('#my_item_'+items[idx].user_item_id).bind('vclick', ... 
$('#my_item_'+items[idx].user_item_id).bind('taphold', ...

在这个例子中,点击实际上并没有在点击之后被调用..在这里检查它:http://jsfiddle.net/YL8hj/43/

编辑:

jQuery Mobile 自动将触摸事件绑定到某些元素。什么时候 将 iScroll 与 jQuery Mobile 结合起来可能是一个好主意 将功能与“touchmove”事件分开并防止该事件发生 冒泡( event.preventDefault() )。通过这样做 jQuery Mobile 用户交互时将无法处理触摸事件 使用 iScroll 元素。

还有http://appcropolis.com/blog/jquery-wrapper-for-iscroll/ 信用https://***.com/a/9408567/643500

【讨论】:

我试过了,它似乎在做同样的事情。 :/ 这些项目在 iScroll 对象内。我想知道这是否会导致问题? 好的,我在设备上尝试了那个 jsfiddle 链接,但它不能正常工作。所有事件都在 android webview 中触发。所以是设备问题。我需要想出一种不同的方式来设置这些事件...... 请再次尝试 jsfiddle。我离开了水龙头,点击和 vclick 只是为了显示差异。 js fiddle 仍然无法在设备上运行。在这一点上,我认为 iScroll 与这个问题没有任何关系。

以上是关于点击事件后jQuery调用点击事件的主要内容,如果未能解决你的问题,请参考以下文章

jQuery:绑定和取消绑定实时点击事件

分页后数据表jquery点击事件不起作用

使用javascript / jQuery将触摸事件应用于每次点击事件

click事件的累加绑定,绑定一次点击事件,执行多次

在jq中,当触发点击事件后,在此事件没完成之前,不能再触发点击事件,在事件完成后才能再次点击,该怎么弄

在ajax调用中附加tbody后点击事件不起作用?