Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery calling click event after taphold event

I'm developing a PhoneGap app for Android using Jquery and Jquery Mobile.

I've got a list of items that need two events bound to each item in the list. I need a "taphold" event and a "click" event. The problem I'm having is when I do a "taphold", the correct "taphold" event is fired. However, as soon as I release, the click event is also fired. How can I prevent the click event from firing after a taphold?

Code:

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

After the advice below, Here is what I ended up with. This works inside the iScroll object.

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();
}
like image 541
jasonlg3d Avatar asked May 08 '12 16:05

jasonlg3d


1 Answers

Rather than use tap and taphold (which I've tried to use but ran into the same problems, it seems to be an inherent issue with the taphold event) you can use vmousedown and set a flag, then bind to vmouseup to determine if it was a tap or a 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]);
        }
    }
});

For this to work properly you'll have to add an IIFE around the loop-code or change ShowMyItemInfo(items[idx]); to work without referencing the variable that changes each iteration of the loop. An easy to create an IIFE is to just use $.each(). Otherwise your loop would look something like this:

for(var idx in items)
{
    (function (idx) {
        ...
    })(idx);
}

IIFE = Immediately-Invoked-Function-Expression. It allows us to take a "snapshot" of the current state of variables we pass into the IIFE. So as we pass in idx (technically the second instance is the variable that's being passed in, and the first instance is the variable available inside the IIFE, which could be changed to something like ids_new for simplicity sake), the value passed in is saved for when the tap event handler fires.

Update

You can also set a timeout to determine taphold rather than using the vmouseup event:

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

This way the event will fire after the user holds down their finger for three seconds. Then the tap event handler will only fire if that three seconds did not occur.

like image 82
Jasper Avatar answered Sep 26 '22 01:09

Jasper