A little journey into touch events and transitions

I’ve been doing a bit of R&D into some badge-related mobile stuff, and playing with touch events to get some swipe-like behaviour. While this is more of a gripe than a tutorial, I’ll go through the process anyway, for those of you following along at home.

First things first, we have to listen for a touchstart event…

var swipeStartLocation, swipeStartTime,
    minSwipeDistance = 50, // pixels
    maxSwipeDuration = 1000; // ms

container.addEventListener('touchstart', function(e) {
    swipeStartLocation = {x: e.touches[0].pageX, y: e.touches[0].pageY};
    swipeStartTime = e.timeStamp;
});

We can then listen for a touchend event, and check the distance and duration to confirm the action is what we’re looking for.

container.addEventListener('touchend', function(e) {
    var touch = e.touches[0] || e.changedTouches[0],
        xLocation = (touch || {pageX:swipeStartLocation.x}).pageX,
        hDistance = xLocation - swipeStartLocation.x,
        // Don't need these personally, but you might
        // yLocation = (touch || {pageY:swipeStartLocation.y}).pageY,
        // vDistance = yLocation - swipeStartLocation.y,
        time = e.timeStamp,
        duration = time - swipeStartTime;

    if (duration <= maxSwipeDuration && Math.abs(hDistance) >= minSwipeDistance) {
        // Do some action based on the input
    }
});

This works fine on my phone, giving me the desired effect, but that’s running Android 2.2.2. When I open it up on my tablet, which is up-to-date, it fails completely. After a bit of poking around, I worked out that you had to listen for a touchmove event, and prevent that from being natively handled.

container.addEventListener('touchmove', function(e) {
    e.preventDefault();
});

And now swiping is working properly on the tablet too. It’s probably worth noting though that doing this prevents the user from scrolling. So if you’re only interested in swiping in one direction (as I am), you’ll probably want something more akin to this:

container.addEventListener('touchmove', function(e) {
    var xDiff = Math.abs(e.touches[0].pageX - swipeStartLocation.x),
        yDiff = Math.abs(e.touches[0].pageY - swipeStartLocation.y);

    if (xDiff > yDiff) {
        // We're swiping horizontally
        e.preventDefault();
    }
});

‘But what about that gripe?!’, I hear you ask. Well, in this instance, the ‘some action’ noted above assigns previous, current and next classes to various items, which are positioned appropriately with a bit of CSS. To make things a bit nicer, there are some transitions being applied, so the items appear to scroll (without me having to do any heavy lifting). This was all working fine, until I added the touchmove handler, but now my swipe-able items don’t update properly on my phone, until the browser is forced to re-render.

Fortunately, Android 2.2.2 doesn’t support transition, and requires a -webkit prefix. So after a lot of frustrated poking about, the simplest solution is to just remove -webkit-transition from the CSS altogether, and slightly reduce the experience for people using old Android devices. It’s not the end of the world, and while it’s a bit nicer to have things moving around the screen as you interact, it’s nicer still to actually see the item you’re supposed to be looking at!

Caveat lector: I don’t have an iOS device to hand (note to self: go into the office more frequently!), so I’ve not tested this in any version of Mobile Safari. But it should work as promised.

Your email address will not be published. Required fields are marked *

HTML is not allowed (it'll appear as typed), but you can use Markdown instead. For the unitiated, paragraphs will be auto-inserted, but links won't be converted unless you [write them properly](http://example.com/).