Detect left/right-swipe on touch-devices, but allow up/down-scrolling Detect left/right-swipe on touch-devices, but allow up/down-scrolling jquery jquery

Detect left/right-swipe on touch-devices, but allow up/down-scrolling


I wrote my own touch handler events.maybe this helps you

it checks for:

fast click : 'fc'

swipe left : 'swl'

swipe right : 'swr'

swipe up : 'swu'

swipe down : 'swd'

each check initializes it's correspondent event.but you can scroll and do whatever else you do normally. you just have some new events.

you need swl swr, I aslo suggest to use fc (fastclick) for click events... it's much faster than normal click.

window.onload = function() {    (function(d) {        var            ce = function(e, n) {                var a = document.createEvent("CustomEvent");                a.initCustomEvent(n, true, true, e.target);                e.target.dispatchEvent(a);                a = null;                return false            },            nm = true,            sp = {                x: 0,                y: 0            },            ep = {                x: 0,                y: 0            },            touch = {                touchstart: function(e) {                    sp = {                        x: e.touches[0].pageX,                        y: e.touches[0].pageY                    }                },                touchmove: function(e) {                    nm = false;                    ep = {                        x: e.touches[0].pageX,                        y: e.touches[0].pageY                    }                },                touchend: function(e) {                    if (nm) {                        ce(e, 'fc')                    } else {                        var x = ep.x - sp.x,                            xr = Math.abs(x),                            y = ep.y - sp.y,                            yr = Math.abs(y);                        if (Math.max(xr, yr) > 20) {                            ce(e, (xr > yr ? (x < 0 ? 'swl' : 'swr') : (y < 0 ? 'swu' : 'swd')))                        }                    };                    nm = true                },                touchcancel: function(e) {                    nm = false                }            };        for (var a in touch) {            d.addEventListener(a, touch[a], false);        }    })(document);    //EXAMPLE OF USE    var h = function(e) {        console.log(e.type, e)    };    document.body.addEventListener('fc', h, false); // 0-50ms vs 500ms with normal click    document.body.addEventListener('swl', h, false);    document.body.addEventListener('swr', h, false);    document.body.addEventListener('swu', h, false);    document.body.addEventListener('swd', h, false);}

in this case h is my handler for every type of event and i add the handlers to the body.

for what i understand your question you just have to write

YOURELEMENT.addEventListener('swr',YOURSWIPERIGHTFUNCTION,false);YOURELEMENT.addEventListener('swl',YOURSWIPELEFTFUNCTION,false);

to handle multiple elements and the same function... just add one handler.

so if you have

<ul id="ul"><li>1</li><li>2</li><li>3</li></ul>

you do:

var deleteli=function(e){    var li=e.target;    console.log('deleting '+li.textContent);}document.getElementById('ul').addEventListener('swl',deleteli,false);

same for fc & swr

there is a bug in ios: don't use alert() .. it will execute 2 times.


There is a "bug" in the accepted answer. If you don't use Chrome on Android but the build in browser or a "webview" (For a html5-hybrid-app) for example, then the swipe is not being detected.

I found out that the event doesn't fire, because of the normal scroll behavior. So adding "e.preventDefault();" in touchmove would fix it or the fix from Eric Fuller in the accepted answer.

It's a nice snipped but in a mobile WebApp or Website this could result in a bad scroll stuttering, because the touch-events are observed the whole time.

So I decided to build something new. It's not as comfortable like to have new event listeners, but it's comfortable enough for my needs and it's performat.

function detectswipe(el,func) {  swipe_det = new Object();  swipe_det.sX = 0;  swipe_det.sY = 0;  swipe_det.eX = 0;  swipe_det.eY = 0;  var min_x = 20;  //min x swipe for horizontal swipe  var max_x = 40;  //max x difference for vertical swipe  var min_y = 40;  //min y swipe for vertical swipe  var max_y = 50;  //max y difference for horizontal swipe  var direc = "";  ele = document.getElementById(el);  ele.addEventListener('touchstart',function(e){    var t = e.touches[0];    swipe_det.sX = t.screenX;     swipe_det.sY = t.screenY;  },false);  ele.addEventListener('touchmove',function(e){    e.preventDefault();    var t = e.touches[0];    swipe_det.eX = t.screenX;     swipe_det.eY = t.screenY;      },false);  ele.addEventListener('touchend',function(e){    //horizontal detection    if ((((swipe_det.eX - min_x > swipe_det.sX) || (swipe_det.eX + min_x < swipe_det.sX)) && ((swipe_det.eY < swipe_det.sY + max_y) && (swipe_det.sY > swipe_det.eY - max_y)))) {      if(swipe_det.eX > swipe_det.sX) direc = "r";      else direc = "l";    }    //vertical detection    if ((((swipe_det.eY - min_y > swipe_det.sY) || (swipe_det.eY + min_y < swipe_det.sY)) && ((swipe_det.eX < swipe_det.sX + max_x) && (swipe_det.sX > swipe_det.eX - max_x)))) {      if(swipe_det.eY > swipe_det.sY) direc = "d";      else direc = "u";    }    if (direc != "") {      if(typeof func == 'function') func(el,direc);    }    direc = "";  },false);  }myfunction(el,d) {  alert("you swiped on element with id '"+el+"' to "+d+" direction");}

To use the function just use it like

detectswipe('an_element_id',myfunction);detectswipe('an_other_element_id',my_other_function);

If a swipe is detected the function "myfunction" is called with parameter element-id and "l,r,u,d" (left,right,up,down).

Example: http://jsfiddle.net/rvuayqeo/1/


Inspired by @cocco I created a better (non-minimized) version:

(function(d) {    // based on original source: https://stackoverflow.com/a/17567696/334451    var newEvent = function(e, name) {        // This style is already deprecated but very well supported in real world: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent        // in future we want to use CustomEvent function: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent        var a = document.createEvent("CustomEvent");        a.initCustomEvent(name, true, true, e.target);        e.target.dispatchEvent(a);        a = null;        return false    };    var debug = false; // emit info to JS console for all touch events?    var active = false; // flag to tell if touchend should complete the gesture    var min_gesture_length = 20; // minimum gesture length in pixels    var tolerance = 0.3; // value 0 means pixel perfect movement up or down/left or right is required, 0.5 or more means any diagonal will do, values between can be tweaked    var sp = { x: 0, y: 0, px: 0, py: 0 }; // start point    var ep = { x: 0, y: 0, px: 0, py: 0 }; // end point    var touch = {        touchstart: function(e) {            active = true;            t = e.touches[0];            sp = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };            ep = sp; // make sure we have a sensible end poin in case next event is touchend            debug && console.log("start", sp);        },        touchmove: function(e) {            if (e.touches.length > 1) {                active = false;                debug && console.log("aborting gesture because multiple touches detected");                return;            }            t = e.touches[0];            ep = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };            debug && console.log("move", ep, sp);        },        touchend: function(e) {            if (!active)                return;            debug && console.log("end", ep, sp);            var dx = Math.abs(ep.x - sp.x);            var dy = Math.abs(ep.y - sp.y);            if (Math.max(dx, dy) < min_gesture_length) {                debug && console.log("ignoring short gesture");                return; // too short gesture, ignore            }            if (dy > dx && dx/dy < tolerance && Math.abs(sp.py - ep.py) > min_gesture_length) { // up or down, ignore if page scrolled with touch                newEvent(e, (ep.y - sp.y < 0 ? 'gesture-up' : 'gesture-down'));                //e.cancelable && e.preventDefault();            }            else if (dx > dy && dy/dx < tolerance && Math.abs(sp.px - ep.px) > min_gesture_length) { // left or right, ignore if page scrolled with touch                newEvent(e, (ep.x - sp.x < 0 ? 'gesture-left' : 'gesture-right'));                //e.cancelable && e.preventDefault();            }            else {                debug && console.log("ignoring diagonal gesture or scrolled content");            }            active = false;        },        touchcancel: function(e) {            debug && console.log("cancelling gesture");            active = false;        }    };    for (var a in touch) {        d.addEventListener(a, touch[a], false);        // TODO: MSIE touch support: https://github.com/CamHenlin/TouchPolyfill    }})(window.document);

Important changes compared to original version by @cocco:

  • use event.touches[0].screenX/screenY as the major source of information. The pageX/pageY properties do not correctly represent the movement of touches on screen because if some piece of page scrolls with the touch, it affects the pageX/pageY values, too.
  • add minimum gesture length setting
  • add tolerance setting for ignoring near diagonal gestures
  • ignore the gesture if page content has scrolled with the gesture (inspect difference in pageX/pageY before triggering gesture)
  • abort gesture if multiple touches are done during the gesture

Things that would need to be done in the future:

  • use CustomEvent() function interface instead of createEvent() method.
  • add MSIE compatibility
  • maybe configure minimum gesture length for pageX/pageY separate from screenX/screenY?
  • It seems that Chrome's threaded scrolling still causes some problems with scrolling detection if touch movement is too fast. Perhaps wait for next frame before deciding where scrolling has gone before deciding if event should be triggered?

Usage is as follows:

document.body.addEventListener('gesture-right', function (e) {  ... });

or jquery style

$("article").on("gesture-down", function (e) { ... });