Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A simple swipe detection on vanilla js
var touchstartX = 0;
var touchstartY = 0;
var touchendX = 0;
var touchendY = 0;
var gesuredZone = document.getElementById('gesuredZone');
gesuredZone.addEventListener('touchstart', function(event) {
touchstartX = event.screenX;
touchstartY = event.screenY;
}, false);
gesuredZone.addEventListener('touchend', function(event) {
touchendX = event.screenX;
touchendY = event.screenY;
handleGesure();
}, false);
function handleGesure() {
var swiped = 'swiped: ';
if (touchendX < touchstartX) {
alert(swiped + 'left!');
}
if (touchendX > touchstartX) {
alert(swiped + 'right!');
}
if (touchendY < touchstartY) {
alert(swiped + 'down!');
}
if (touchendY > touchstartY) {
alert(swiped + 'left!');
}
if (touchendY == touchstartY) {
alert('tap!');
}
}
@vokeio

This comment has been minimized.

Copy link

vokeio commented Oct 31, 2016

Hello,

This api event.screen does not work any more.

Change to e.changedTouches[0].screenY; and e.changedTouches[0].screenX;

Also line 31 should say up.

@angeenes

This comment has been minimized.

Copy link

angeenes commented Nov 15, 2016

Actually, in this script, it should be : event.changedTouches[0].screenY; and event.changedTouches[0].screenX;

@prochac

This comment has been minimized.

Copy link

prochac commented Dec 26, 2016

THX
with some errors, but still better than jQuery

@cclifford3

This comment has been minimized.

Copy link

cclifford3 commented Jun 14, 2017

@SleepWalker also line 31 should be the 'up' direction

@DZuz14

This comment has been minimized.

Copy link

DZuz14 commented Aug 20, 2017

Useless.

@alextrastero

This comment has been minimized.

Copy link

alextrastero commented Nov 12, 2017

Worked for me thanks! :)

@mocon

This comment has been minimized.

Copy link

mocon commented Dec 5, 2017

Thanks! I made some small tweaks to this, based on the comments above:

let touchstartX = 0;
let touchstartY = 0;
let touchendX = 0;
let touchendY = 0;

const gestureZone = document.getElementById('gestureZone');

gestureZone.addEventListener('touchstart', function(event) {
    touchstartX = event.changedTouches[0].screenX;
    touchstartY = event.changedTouches[0].screenY;
}, false);

gestureZone.addEventListener('touchend', function(event) {
    touchendX = event.changedTouches[0].screenX;
    touchendY = event.changedTouches[0].screenY;
    handleGesture();
}, false); 

function handleGesture() {
    if (touchendX <= touchstartX) {
        console.log('Swiped left');
    }
    
    if (touchendX >= touchstartX) {
        console.log('Swiped right');
    }
    
    if (touchendY <= touchstartY) {
        console.log('Swiped up');
    }
    
    if (touchendY >= touchstartY) {
        console.log('Swiped down');
    }
    
    if (touchendY === touchstartY) {
        console.log('Tap');
    }
}
@Demven

This comment has been minimized.

Copy link

Demven commented Dec 7, 2017

Thank you, it was very helpful!

@AmicoJoules

This comment has been minimized.

Copy link

AmicoJoules commented Dec 18, 2017

Thx!

@peterver

This comment has been minimized.

Copy link

peterver commented Jan 29, 2018

I have a different approach for the handleGesture ( does not include the tap though ) that works with the dimensions of the element that the events are occuring on ( because let's face it, users do not swipe in a straight line most of the time, and we should account for this )

const { width, height } = dom_el.getBoundingClientRect();

const ratio_horizontal = (end.screenX - start.screenX) / width;
const ratio_vertical = (end.screenY - start.screenY) / height;

if (ratio_horizontal > ratio_vertical && ratio_horizontal > 0.25) {
    return console.log('swipe-right');
}
if (ratio_vertical > ratio_horizontal && ratio_vertical > 0.25) {
    return console.log('swipe-down');
}
if (ratio_horizontal < ratio_vertical && ratio_horizontal < -0.25) {
    return console.log('swipe-left');
}
if (ratio_vertical < ratio_horizontal && ratio_vertical < -0.25) {
    return console.log('swipe-up');
}

return false;
@IanRr

This comment has been minimized.

Copy link

IanRr commented Feb 8, 2018

@mocon heads up on your revisions if you haven't already noticed

Seems to me that including using touchendX <= touchstartX etc instead of touchendX < touchstartX etc in the handleGesture code means that any "tap" will also register every swipe event

@stephenjude

This comment has been minimized.

Copy link

stephenjude commented Apr 14, 2018

@IanRr thanks for that correction. @mocon thanks for tweak
It works fine now

let touchstartX = 0;
let touchstartY = 0;
let touchendX = 0;
let touchendY = 0;

const gestureZone = document.getElementById('modalContent');

gestureZone.addEventListener('touchstart', function(event) {
    touchstartX = event.changedTouches[0].screenX;
    touchstartY = event.changedTouches[0].screenY;
}, false);

gestureZone.addEventListener('touchend', function(event) {
    touchendX = event.changedTouches[0].screenX;
    touchendY = event.changedTouches[0].screenY;
    handleGesture();
}, false); 

function handleGesture() {
    if (touchendX < touchstartX) {
        console.log('Swiped left');
    }
    
    if (touchendX > touchstartX) {
        console.log('Swiped right');
    }
    
    if (touchendY < touchstartY) {
        console.log('Swiped up');
    }
    
    if (touchendY > touchstartY) {
       console.log('Swiped down');
    }
    
    if (touchendY === touchstartY) {
       console.log('Tap');
    }
}
@c7x43t

This comment has been minimized.

Copy link

c7x43t commented May 3, 2018

Small modification of @stephenjude using angles (45° for each of the 8 directions) to determine the direction and a treshold of 1px or 1% of page width:

let pageWidth = window.innerWidth || document.body.clientWidth;
let treshold = Math.max(1,Math.floor(0.01 * (pageWidth)));
let touchstartX = 0;
let touchstartY = 0;
let touchendX = 0;
let touchendY = 0;

const limit = Math.tan(45 * 1.5 / 180 * Math.PI);
const gestureZone = document.getElementById('modalContent');

gestureZone.addEventListener('touchstart', function(event) {
    touchstartX = event.changedTouches[0].screenX;
    touchstartY = event.changedTouches[0].screenY;
}, false);

gestureZone.addEventListener('touchend', function(event) {
    touchendX = event.changedTouches[0].screenX;
    touchendY = event.changedTouches[0].screenY;
    handleGesture(event);
}, false);

function handleGesture(e) {
    let x = touchendX - touchstartX;
    let y = touchendY - touchstartY;
    let xy = Math.abs(x / y);
    let yx = Math.abs(y / x);
    if (Math.abs(x) > treshold || Math.abs(y) > treshold) {
        if (yx <= limit) {
            if (x < 0) {
                console.log("left");
            } else {
                console.log("right");
            }
        }
        if (xy <= limit) {
            if (y < 0) {
                console.log("top");
            } else {
                console.log("bottom");
            }
        }
    } else {
        console.log("tap");
    }
}
@artgolwebdev

This comment has been minimized.

Copy link

artgolwebdev commented Jun 6, 2018

Like !

@behnammodi

This comment has been minimized.

Copy link

behnammodi commented Aug 27, 2018

i add this script to npm

npm link:
https://www.npmjs.com/package/xwiper

github link:
https://github.com/uxitten/xwiper

@adsoft-solutions

This comment has been minimized.

Copy link

adsoft-solutions commented Sep 5, 2018

Hello , actually won't work on some Android Chrome newer versions... If someone could add browser interogation for Pointer Events and a solution for the it , would be our hero !

@alexPalita

This comment has been minimized.

Copy link

alexPalita commented Sep 6, 2018

Idk... I tried to modify uxitten approach with Pointer Events ... If someone could write a more elegant version of this with pinch and multiple taps would be awesome ...

class Xwiper {
    constructor(element) {
        this.element = null;
        this.touchStartX = 0;
        this.touchStartY = 0;
        this.touchEndX = 0;
        this.touchEndY = 0;
        this.touchMovedX = 0;
        this.touchMovedY = 0;
        this.sensitive = 5;
        this.onSwipeLeftAgent = null;
        this.onSwipeRightAgent = null;
        this.onSwipeUpAgent = null;
        this.onSwipeDownAgent = null;
        this.onTapAgent = null;
        this.onTouchStart = this.onTouchStart.bind(this);
        this.onTouchEnd = this.onTouchEnd.bind(this);
        this.onPointerStart = this.onPointerStart.bind(this);
        this.onPointerMoved = this.onPointerMoved.bind(this);
        this.onPointerEnd = this.onPointerEnd.bind(this);
        this.onSwipeLeft = this.onSwipeLeft.bind(this);
        this.onSwipeRight = this.onSwipeRight.bind(this);
        this.onSwipeUp = this.onSwipeUp.bind(this);
        this.onSwipeDown = this.onSwipeDown.bind(this);
        this.onTap = this.onTap.bind(this);
        this.destroy = this.destroy.bind(this);
        this.handleGesture = this.handleGesture.bind(this);

        this.element = document.querySelector(element);
		if (window.PointerEvent) {
			this.element.addEventListener( 'pointerdown', this.onPointerStart,false);

			this.element.addEventListener( 'pointermove', this.onPointerMoved,false);
				
			this.element.addEventListener( 'pointercancel', this.onPointerEnd,false);
		} else {
			this.element.addEventListener('touchstart', this.onTouchStart ,false);
			this.element.addEventListener('touchend', this.onTouchEnd, false);	
		}
    }

    onTouchStart(event) {
        this.touchStartX = event.changedTouches[0].screenX;
        this.touchStartY = event.changedTouches[0].screenY;
    }

    onTouchEnd(event) {
        this.touchEndX = event.changedTouches[0].screenX;
        this.touchEndY = event.changedTouches[0].screenY;
        this.handleGesture();
    }

    onPointerStart(event) {
		switch ( event.pointerType ) {
			case 'mouse':
				//add code
			break;

			case 'touch':
				this.touchStartX = event.screenX;
				this.touchStartY = event.screenY;
			break;
			case 'pen':
				//add code
			break;
			default:
				//add code
			break;
			}
    }

    onPointerMoved(event) {
		switch ( event.pointerType ) {
			case 'mouse':
				//add code
			break;
			case 'touch':
				this.touchMovedX = event.screenX;
				this.touchMovedY = event.screenY;
			break;
			case 'pen':
				//add code
			break;
			default:
				//add code
			break;
		}
    }
    onPointerEnd(event) {
		switch ( event.pointerType ) {
			case 'mouse':
				//add code
			break;

			case 'touch':
				this.touchEndX = this.touchMovedX;
				this.touchEndY = this.touchMovedY;
			break;

			case 'pen':
				//add code
			break;

			default:
				//add code
			break;
		}
        this.handleGesture();
    }

    onSwipeLeft(func) {
        this.onSwipeLeftAgent = func;
    }
    onSwipeRight(func) {
        this.onSwipeRightAgent = func;
    }
    onSwipeUp(func) {
        this.onSwipeUpAgent = func;
    }
    onSwipeDown(func) {
        this.onSwipeDownAgent = func;
    }
    onTap(func) {
        this.onTapAgent = func;
    }

    destroy() {
        this.element.removeEventListener('touchstart', this.onTouchStart);
        this.element.removeEventListener('touchend', this.onTouchEnd);
        this.element.removeEventListener('pointerstart', this.onTouchStart);
        this.element.removeEventListener('pointermove', this.onTouchMove);
        this.element.removeEventListener('touchover', this.onTouchEnd);
    }

	handleGesture() {
        /**
         * swiped left
         */
        if (this.touchEndX + this.sensitive < this.touchStartX) {
            this.onSwipeLeftAgent &&
                this.onSwipeLeftAgent();
            return 'swiped left';
        }

        /**
         * swiped right
         */
        if (this.touchEndX - this.sensitive > this.touchStartX) {
            this.onSwipeRightAgent &&
                this.onSwipeRightAgent();
            return 'swiped right';
        }

        /**
         * swiped up
         */
        if (this.touchEndY + this.sensitive < this.touchStartY) {
            this.onSwipeUpAgent &&
                this.onSwipeUpAgent();
            return 'swiped up';
        }

        /**
         * swiped down
         */
        if (this.touchEndY - this.sensitive > this.touchStartY) {
            this.onSwipeDownAgent &&
                this.onSwipeDownAgent();
            return 'swiped down';
        }

        /**
         * tap
         */
        if (this.touchEndY === this.touchStartY) {
            this.onTapAgent &&
                this.onTapAgent();
            return 'tap';
        }
    }
}
@hperrin

This comment has been minimized.

Copy link

hperrin commented Sep 7, 2018

I took a shot at improving this too. I added multiple listeners support, long press, mouse support, velocity threshold, and customization. It's on NPM and GitHub:

https://www.npmjs.com/package/tinygesture
https://github.com/sciactive/tinygesture

Here's a demo:
https://sciactive.github.io/tinygesture/

Thank you @SleepWalker, @uxitten, @c7x43t.

@javierlog08

This comment has been minimized.

Copy link

javierlog08 commented Jan 6, 2019

Thank you so much.
Pretty usefull !~

@tlacaelelrl

This comment has been minimized.

Copy link

tlacaelelrl commented Apr 25, 2019

Here I leave you some code I use, some of it was initially based on the code listed here, then modified to adjust to my needs and new events.

TlakDev.EventHandler.prototype._defaults = {
	touch: {
		x: 0,
		y: 0
	},
	touchEnd: {
		x: 0,
		y: 0
	}
}
TlakDev.EventHandler.prototype.sendTouchEvent = function (event, element) {
	let t = this;
	switch(true){
		case ((Math.abs(t.touch.x) - Math.abs(t.touchEnd.x)) === 0) && ((Math.abs(t.touch.y) - Math.abs(t.touchEnd.y)) === 0):
			event.direction = "tap";
		break;
		case Math.abs(t.touch.x - t.touchEnd.x) > Math.abs(t.touch.y - t.touchEnd.y):
			/*left/right*/
			if(t.touch.x > t.touchEnd.x){
				event.direction = "left";
			} else {
				event.direction = "right";
			}
		break;
		default:
			/*up/down*/
			if(t.touch.y < t.touchEnd.y){
				event.direction = "down";
			} else {
				event.direction = "up";
			}
		break;
	}
	switch(true){
		case ((Math.abs(t.touch.x) - Math.abs(t.touchEnd.x)) === 0) && ((Math.abs(t.touch.y) - Math.abs(t.touchEnd.y)) === 0):
			event.directionPrecision = "tap";
		break;
		case t.touch.x > t.touchEnd.x:
			/*left*/
			if(t.touch.y > t.touchEnd.y){
				event.directionPrecision = "upLeft";
			} else {
				event.directionPrecision = "downLeft";
			}
		break;
		default:
			/*right*/
			if(t.touch.y > t.touchEnd.y){
				event.directionPrecision = "upRight";
			} else {
				event.directionPrecision = "downRight";
			}
		break;
	}
	/*
	 * Do whatever you need with the event/element
	 * The event contains 2 properties
	 * 1. direction  = tap|left|right|up|down
	 * 2. directionPrecision tap|upRight|upLeft|downRight|downLeft
	*/
}
TlakDev.EventHandler.prototype.setTouch = function (event) {
	let e = {};
	if(event.screenY){
		e.x = event.screenX;
		e.y = event.screenY;
	} else if(typeof event.touches !== "undefined" && event.touches.length > 0){
		e.x = event.touches[0].pageX;
		e.y = event.touches[0].pageY;
	} else if(typeof event.changedTouches !== "undefined" && event.changedTouches.length > 0) {
		e.x = event.changedTouches[0].pageX;
		e.y = event.changedTouches[0].pageY;
	} else {
		e.x = 0;
		e.y = 0;
	}
	return e;
}
/*
@param nodes is an array of javascript elements to which apply the events
*/
TlakDev.EventHandler.prototype.handleSwipes = function (nodes) {
	let t = this;
	nodes.forEach(function(element){
		element.addEventListener("touchstart", function (event) {
			let e = t.setTouch(event);
			t.touch = e;
		});
		element.addEventListener("touchend", function (event) {
			let e = t.setTouch(event);
			t.touchEnd = e;
			t.sendTouchEvent(event, element);
		});
	});
}
@evrenakar

This comment has been minimized.

Copy link

evrenakar commented May 8, 2019

Function that only listen the y down event.

`
var swipe = document.getElementById('swipe');

  var touchstartY = 0;
  var touchendY = 0;

  swipe.addEventListener('touchstart', function(event) {
    touchstartY = event.changedTouches[0].screenY;
  }, false);

  swipe.addEventListener('touchend', function(event) {
    touchendY = event.changedTouches[0].screenY;
      handleSwipe();
  }, false); 

  function handleSwipe() {
      var swiped = 'swiped: ';
      if (touchendY > touchstartY) {
          alert(swiped + 'down!');
      }
  }

`

@karol2001965

This comment has been minimized.

Copy link

karol2001965 commented Jul 30, 2019

Thanks a lot ;)

@jonaed1230

This comment has been minimized.

Copy link

jonaed1230 commented Mar 13, 2020

Thanks a lot it saved my day.

@3rdSenna

This comment has been minimized.

Copy link

3rdSenna commented Apr 19, 2020

@IanRr thanks for that correction. @mocon thanks for tweak
It works fine now

let touchstartX = 0;
let touchstartY = 0;
let touchendX = 0;
let touchendY = 0;

const gestureZone = document.getElementById('modalContent');

gestureZone.addEventListener('touchstart', function(event) {
    touchstartX = event.changedTouches[0].screenX;
    touchstartY = event.changedTouches[0].screenY;
}, false);

gestureZone.addEventListener('touchend', function(event) {
    touchendX = event.changedTouches[0].screenX;
    touchendY = event.changedTouches[0].screenY;
    handleGesture();
}, false); 

function handleGesture() {
    if (touchendX < touchstartX) {
        console.log('Swiped left');
    }
    
    if (touchendX > touchstartX) {
        console.log('Swiped right');
    }
    
    if (touchendY < touchstartY) {
        console.log('Swiped up');
    }
    
    if (touchendY > touchstartY) {
       console.log('Swiped down');
    }
    
    if (touchendY === touchstartY) {
       console.log('Tap');
    }
}

THanks, man. It was really helpfull

@tryhardest

This comment has been minimized.

Copy link

tryhardest commented Jun 22, 2020

This is starting to look like one of the leading script threads on vanillaJS touch events. Seems like Hammer, Zing and Interactjs (pointer events) libs (interact excluded) are maybe become a bit obsolete except for drag, drop, and pointer support. Does anyone have any flushed out changes they have made since (as the thread goes back a few years)? Would be cool to keep this thread up to date with any changes or learnings. Anyone using this extensively with event listeners on touchMove? @evrenakar @tlacaelelrl @hperrin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.