JavaScript on iOS: preventDefault on touchstart without disabling scrolling

33,241

Solution 1

Sometimes you have to ask a question on stack overflow to find the answer yourself. There is indeed a solution to my problem, and it's as follows:

$(document).ready(function() {

    var timeoutId = 0;

    $('img').on('touchstart', function(event) {

        var imgElement = this;

        timeoutId = setTimeout(function() {           

            $(imgElement).one('click', function(event) {
                event.preventDefault();
            });

            /* Show message ... */

        }, 1000);

    }).on('touchend touchcancel', function(event) {                                
        clearTimeout(timeoutId);
    });
});

Explanation

  • No preventDefault() in the touch event handlers. This brings back scrolling behavior (of course).
  • Handle a normal click event once if the message appeared, and prevent it's default action.

Solution 2

You put me on the right track Stefan, having me think the other way around. For anyone still scratching their head over this, here's my solution.

I was trying to allow visitors to scroll through images horizontally, without breaking vertical scrolling. But I was executing custom functionality and waiting for a vertical scroll to happen. Instead, we should allow regular behavior first and wait for a specific gesture to happen like Stefan did.

For example:

$("img").on("touchstart", function(e) {
    var touchStart = touchEnd = e.originalEvent.touches[0].pageX;

    var touchExceeded = false;

    $(this).on("touchmove", function(e) {
        touchEnd = e.originalEvent.touches[0].pageX;

        if(touchExceeded || touchStart - touchEnd > 50 || touchEnd - touchStart > 50) {
            e.preventDefault();

            touchExceeded = true;

            // Execute your custom function.
        }
    });

    $(this).on("touchend", function(e) {
        $(this).off("touchmove touchend");
    });
});

So basically we allow default behavior until the horizontal movement exceeds 50 pixels.

The touchExceeded variable makes sure our function still runs if we re-enter the initial < 50 pixel area.

(Note this is example code, e.originalEvent.touches[0].pageX is NOT cross browser compatible.)

Solution 3

You could look at a gesture library like hammer.js which covers all of the main gesture events across devices.

Share:
33,241
Stefan
Author by

Stefan

Interaction Design, web- &amp; iOS development. http://stefan.pauwels.ch http://zoziapps.ch/slowfeeds

Updated on July 10, 2022

Comments

  • Stefan
    Stefan almost 2 years

    I am working with JavaScript and jQuery in an UIWevView on iOS.

    I'v added some javascript event handler that allow me to capture a touch-and-hold event to show a message when someone taps an img for some time:

    $(document).ready(function() {
    
        var timeoutId = 0;
        var messageAppeared = false;
    
        $('img').on('touchstart', function(event) {
    
            event.preventDefault();
            timeoutId = setTimeout(function() {           
    
                /* Show message ... */
                messageAppeared = true;
    
            }, 1000);
    
        }).on('touchend touchcancel', function(event) {                                
    
            if (messageAppeared) {
                event.preventDefault();
            } else {
                clearTimeout(timeoutId);
            }
            messageAppeared = false;        
        });
    });
    

    This works well to show the message. I added the two "event.preventDefault();" lines to stop imgs inside links to trigger the link.

    The problem is: This also seems to prevent drag events to scroll the page from happen normally, so that the user wouldn't be able to scroll when his swipe happens to begin on an img.

    How could I disable the default link action without interfering with scrolling?

  • Ryan Taylor
    Ryan Taylor almost 9 years
    Just curious, but shouldn't that be one instead of on for touchend? Haven't tried the code, but it seems like it might bind too many times to touchend, while there should be a 1-to-1 touchstart to touchend ratio.
  • Robbert
    Robbert almost 9 years
    Good question. It should unbind itself (and the touchmove binding) instantly, but still, one might indeed work more efficiently. Feel free to mess around with it and see what works best.