Detect a finger swipe through JavaScript on the iPhone and Android

353,714

Solution 1

Simple vanilla JS code sample:

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;

function getTouches(evt) {
  return evt.touches ||             // browser API
         evt.originalEvent.touches; // jQuery
}                                                     
                                                                         
function handleTouchStart(evt) {
    const firstTouch = getTouches(evt)[0];                                      
    xDown = firstTouch.clientX;                                      
    yDown = firstTouch.clientY;                                      
};                                                
                                                                         
function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.touches[0].clientX;                                    
    var yUp = evt.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;
                                                                         
    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            /* right swipe */ 
        } else {
            /* left swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* down swipe */ 
        } else { 
            /* up swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

Tested in Android.

Solution 2

Simple vanilla JS example for horizontal swipe:

let touchstartX = 0
let touchendX = 0
    
function checkDirection() {
  if (touchendX < touchstartX) alert('swiped left!')
  if (touchendX > touchstartX) alert('swiped right!')
}

document.addEventListener('touchstart', e => {
  touchstartX = e.changedTouches[0].screenX
})

document.addEventListener('touchend', e => {
  touchendX = e.changedTouches[0].screenX
  checkDirection()
})

You can use pretty same logic for vertical swipe.

Solution 3

I merged a few of the answers here into a script that uses CustomEvent to fire swiped events in the DOM. Add the 0.7k swiped-events.min.js script to your page and listen for swiped events:

swiped

document.addEventListener('swiped', function(e) {
    console.log(e.target); // the element that was swiped
    console.log(e.detail.dir); // swiped direction
});

swiped-left

document.addEventListener('swiped-left', function(e) {
    console.log(e.target); // the element that was swiped
});

swiped-right

document.addEventListener('swiped-right', function(e) {
    console.log(e.target); // the element that was swiped
});

swiped-up

document.addEventListener('swiped-up', function(e) {
    console.log(e.target); // the element that was swiped
});

swiped-down

document.addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

You can also attach directly to an element:

document.getElementById('myBox').addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

Optional config

You can specify the following attributes to tweak how swipe interaction functions in your page (these are optional).

<div data-swipe-threshold="10"
     data-swipe-timeout="1000"
     data-swipe-ignore="false">
      Swiper, get swiping!
</div>

To set defaults application wide, set config attributes on topmost element:

<body data-swipe-threshold="100" data-swipe-timeout="250">
    <div>Swipe me</div>
    <div>or me</div>
</body>

Source code is available on Github

Solution 4

Based on @givanse's answer, this is how you could do it with classes:

class Swipe {
    constructor(element) {
        this.xDown = null;
        this.yDown = null;
        this.element = typeof(element) === 'string' ? document.querySelector(element) : element;

        this.element.addEventListener('touchstart', function(evt) {
            this.xDown = evt.touches[0].clientX;
            this.yDown = evt.touches[0].clientY;
        }.bind(this), false);

    }

    onLeft(callback) {
        this.onLeft = callback;

        return this;
    }

    onRight(callback) {
        this.onRight = callback;

        return this;
    }

    onUp(callback) {
        this.onUp = callback;

        return this;
    }

    onDown(callback) {
        this.onDown = callback;

        return this;
    }

    handleTouchMove(evt) {
        if ( ! this.xDown || ! this.yDown ) {
            return;
        }

        var xUp = evt.touches[0].clientX;
        var yUp = evt.touches[0].clientY;

        this.xDiff = this.xDown - xUp;
        this.yDiff = this.yDown - yUp;

        if ( Math.abs( this.xDiff ) > Math.abs( this.yDiff ) ) { // Most significant.
            if ( this.xDiff > 0 ) {
                this.onLeft();
            } else {
                this.onRight();
            }
        } else {
            if ( this.yDiff > 0 ) {
                this.onUp();
            } else {
                this.onDown();
            }
        }

        // Reset values.
        this.xDown = null;
        this.yDown = null;
    }

    run() {
        this.element.addEventListener('touchmove', function(evt) {
            this.handleTouchMove(evt).bind(this);
        }.bind(this), false);
    }
}

You can than use it like this:

// Use class to get element by string.
var swiper = new Swipe('#my-element');
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// Get the element yourself.
var swiper = new Swipe(document.getElementById('#my-element'));
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// One-liner.
(new Swipe('#my-element')).onLeft(function() { alert('You swiped left.') }).run();

Solution 5

I have found @givanse brilliant answer to be the most reliable and compatible across multiple mobile browsers for registering swipe actions.

However, there's a change in his code required to make it work in modern day mobile browsers that are using jQuery.

event.toucheswon't exist if jQuery is used and results in undefined and should be replaced by event.originalEvent.touches. Without jQuery, event.touches should work fine.

So the solution becomes,

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;                                                        

function handleTouchStart(evt) {                                         
    xDown = evt.originalEvent.touches[0].clientX;                                      
    yDown = evt.originalEvent.touches[0].clientY;                                      
};                                                

function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.originalEvent.touches[0].clientX;                                    
    var yUp = evt.originalEvent.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            /* left swipe */ 
        } else {
            /* right swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* up swipe */ 
        } else { 
            /* down swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

Tested on:

  • Android: Chrome, UC Browser
  • iOS: Safari, Chrome, UC Browser
Share:
353,714
827
Author by

827

Updated on December 12, 2021

Comments

  • 827
    827 over 2 years

    How can you detect that a user swiped his finger in some direction over a web page with JavaScript?

    I was wondering if there was one solution that would work for websites on both the iPhone and an Android phone.

  • Volomike
    Volomike over 13 years
    I believe it's touchstart, touchmove, touchcancel, and touchend that one would work with, not mousedown or mouseup.
  • Niklas Ekman
    Niklas Ekman over 10 years
    If one does not want jQuery mobile to manipulate the UI, see: stackoverflow.com/questions/8648596/…
  • d.raev
    d.raev about 10 years
    Looks cool and simple, any idea what is the support for this events touchstart, touchmove ?
  • Philip G
    Philip G over 9 years
    I also needed to add: $.event.special.swipe.scrollSupressionThreshold = 8; but you put me in the right direction! Thank you!
  • Codebeat
    Codebeat about 9 years
    It works pretty well but has a problem detecting straight movements. I will post another answer at this topic that fixed this as JQuery (desktop) solution. It also adds the mouse version of these swipe events and add a sensitivity option.
  • Codebeat
    Codebeat about 9 years
    Damn. Topic is closed so cannot add my answer!
  • Peter Eisentraut
    Peter Eisentraut over 8 years
    This works great, but left/right and up/down are backwards.
  • mrid
    mrid over 7 years
    Works great !! Any idea how to get the exact distance swiped ?? I tried using yDiff for swipe down, but it keeps giving me different values for almost equal swipes.
  • nick.skriabin
    nick.skriabin over 7 years
    this code probably wont work because you'll get an exception while trying to call .bind of undefined because your handleTouchMove actually didn't return anything. also it's useless to call bind when calling function with this. because it's already bound to current context
  • quantumpotato
    quantumpotato over 7 years
    I tried this - when I swipe the page scrolls instead of the event triggering
  • Jan Derk
    Jan Derk about 7 years
    originalEvent is a JQuery property. It should be left out if you run pure javascript without JQuery. The current code raises an exception if run without JQuery.
  • Jan Derk
    Jan Derk about 7 years
    originalEvent is a JQuery property. It does not even exist in pure Javascript.
  • nashcheez
    nashcheez about 7 years
    As per this SO answer, a touch event if supported by the browser will be exposed through event.originalEvent. The thing is event.touches has ceased to exist now and results in undefined.
  • Jan Derk
    Jan Derk about 7 years
    event.touches only ceased to exist when using JQuery. Try your code without JQuery and you will get an error that evt.originalEvent is undefined. JQuery totally replaces event with its own and puts the native browser event in originalevent. Short version: Your code only works with JQuery. It works without JQuery if you remove originalevent.
  • nashcheez
    nashcheez about 7 years
    Yeah I researched a bit and realized you were right about the availability of jquery enabling event.originalEvent. I will update my answer. Thanks! :)
  • Ali Ghanavatian
    Ali Ghanavatian almost 7 years
    I just removed .bind(this); and it worked gracefully. thank you @nicholas_r
  • iiic
    iiic over 6 years
    It works good also on desktop Chrome with touchscreen (on Windows). But there's already build-in function makes history back and history forward on same gestures. It's possible to turn it off, but you'r mess with user which expecting different behaviour.
  • StefanBob
    StefanBob about 6 years
    I came here because pure-swipe was not working for me on MOBILE
  • John Doherty
    John Doherty over 5 years
    @StefanBob if you raise a tick on the github repo with enough information to allow me to reproduce the issue, I will look into it
  • Blue Tram
    Blue Tram over 5 years
    Part get the element yourself I just remove '#' in document.getElementById('my-element') and it worked good. Thank you @Marwelln :)
  • TetraDev
    TetraDev about 5 years
    If you want to wait until the swipe ENDS (meaning after they lift their finger or onmouseup), change touches[0] to changedTouches[0] and the event handler type handleTouchMove to handleTouchEnd
  • Mat
    Mat about 5 years
    call run() twice and you get a nasty memory leak
  • kishore kingmaker
    kishore kingmaker over 4 years
    @Marwelln Hi Marwelln, can you say where you got this code from?
  • collimarco
    collimarco over 4 years
    Thanks, it works perfectly! I replaced Hammer.js with your library, because the former doesn't work with browser zoom and that is a serious usability issue. With this library the zoom works properly (tested on Android)
  • collimarco
    collimarco over 4 years
    A problem with this is that it does not consider the size of the gesture: even a small gesture (of a few pixels) may cause an involuntary swipe...
  • 1.21 gigawatts
    1.21 gigawatts over 4 years
    Is this for ES5 or ES6?
  • Trendal Toews
    Trendal Toews over 4 years
    @gigawatts I don't recall. The project that used that has reached EOL already and I haven't needed the code since. I suspect at the time I was writing for ES6 but that was over 2 years ago.
  • NMALM
    NMALM about 4 years
    Currently using this version. How would I prevent this from firing multiple times if swiped in repetition? I utilize this with the animate feature for a sidescrolling form and when I swipe multiple times, things get a bit screwy and my divs start overlapping in the visible area.
  • Panagiss
    Panagiss about 4 years
    if preventDefault() is added the touch controls sometimes get wrong coords!!
  • Kviz Majster
    Kviz Majster almost 4 years
    Changed xDown = evt.originalEvent.touches[0].clientX; yDown = evt.originalEvent.touches[0].clientY; to xDown = evt.offsetX; yDown = evt.offsetY; and now it works like charm on normal JS. I like this solution.
  • user1090751
    user1090751 over 3 years
    how do we know the div where swipe is started?
  • AlexandreS
    AlexandreS over 3 years
    Hammer.js doesn't seem to be maintained anymore
  • Basj
    Basj over 3 years
    @collimarco See my answer below, derived from this one; it solves this problem.
  • Basj
    Basj over 3 years
    If you do a touch "pinch zoom" with two fingers, one finger nearly fixed, and the other finger moving away, I think it is detected as a "swipe" by this code... Isn't it?
  • Vivi
    Vivi over 3 years
    Any chance you can add an example usage?
  • smlnl
    smlnl over 3 years
    How can we make this work for every "class" ? // One-liner. (new Swipe('.everyElementHasThisClass')).onLeft(function() { alert('You swiped left.') }).run();
  • NiceToMytyuk
    NiceToMytyuk over 3 years
    how could i add a range before the swipe event will be casted? like if it's just a small right or left movment i have to ignore it while when it's significant i have to cast it
  • givanse
    givanse over 3 years
    xDiff and yDiff would be the numbers to gate under a treshold
  • codepleb
    codepleb about 3 years
    Lol this is so simple and even allows to specify a "travel distance".
  • Mattia Rasulo
    Mattia Rasulo about 3 years
    Best answer by far.. it's a shame it doesn't have more upvotes..
  • Dgloria
    Dgloria almost 3 years
    This is perfect.
  • Mystogan
    Mystogan over 2 years
    @MattiaRasulo maybe need to add up and down swipe
  • Henry James
    Henry James over 2 years
    this is some real nice code, if you want to do up and down all you need to do is change where it states X and replace with Y. Easyyyy
  • Philipp Moers
    Philipp Moers over 2 years
    Great answer because of its simplicity! It may be worth noting that also vertical swipes are detected as horizontal swipes with this code if touch points have a minimal x distance.
  • Zahidul Islam Ruhel
    Zahidul Islam Ruhel about 2 years
    @JohnDoherty would you extend it so it also works on desktop(non-touch) devices.
  • grepgrok
    grepgrok about 2 years
    It should be noted that "right swipe" is referring to swiping right-to-left, the same goes for the rest of the swipes: [listed]-to-[opposite].