Categories:

Detecting a swipe (left, right, top or down) using touch

Swiping in touch is the act of quickly moving your finger across the touch surface in a certain direction. There is currently no "onswipe" event in JavaScript, which means it's up to us to implement one using the available touch events, plus define just when a swipe is a, well, "swipe".

Lets first define when a movement across the touch surface should be considered a swipe. There are two variables at play here- the distance traveled by the user's finger on the x or y-axis from touchstart to touchend, and, the time it took. Based on these two factors, we can decide whether that action qualifies as a swipe and in what direction.

With that said, lets put ideas into action and see how to go about detecting a swipe right (from left to right). Once we can do that, detecting swipe in the other 3 directions is pretty much identical. For this exercise we'll stipulate that a right swipe has occurred when the user has moved his finger across the touch surface a minimum of 150px horizontally in 200 ms or less from left to right. Furthermore, there should be no more than 100px traveled vertically, to avoid "false positives" whereby the user swipes diagonally across, which we don't want to qualify as a swipe right.

Example (mouse simulation added for non touch devices):

Swipe Me

<script>

window.addEventListener('load', function(){

	var touchsurface = document.getElementById('touchsurface'),
		startX,
		startY,
		dist,
		threshold = 150, //required min distance traveled to be considered swipe
		allowedTime = 200, // maximum time allowed to travel that distance
		elapsedTime,
		startTime

	function handleswipe(isrightswipe){
		if (isrightswipe)
			touchsurface.innerHTML = 'Congrats, you\'ve made a <span style="color:red">right swipe!</span>'
		else{
			touchsurface.innerHTML = 'Condition for right swipe not met yet'
 		}
 	}

	touchsurface.addEventListener('touchstart', function(e){
		touchsurface.innerHTML = ''
		var touchobj = e.changedTouches[0]
		dist = 0
		startX = touchobj.pageX
		startY = touchobj.pageY
		startTime = new Date().getTime() // record time when finger first makes contact with surface
		e.preventDefault()
	}, false)

	touchsurface.addEventListener('touchmove', function(e){
		e.preventDefault() // prevent scrolling when inside DIV
	}, false)

	touchsurface.addEventListener('touchend', function(e){
		var touchobj = e.changedTouches[0]
		dist = touchobj.pageX - startX // get total dist traveled by finger while in contact with surface
		elapsedTime = new Date().getTime() - startTime // get time elapsed
		// check that elapsed time is within specified, horizontal dist traveled >= threshold, and vertical dist traveled <= 100
		var swiperightBol = (elapsedTime <= allowedTime && dist >= threshold && Math.abs(touchobj.pageY - startY) <= 100)
		handleswipe(swiperightBol)
		e.preventDefault()
	}, false)

}, false) // end window.onload
</script>

<div id="touchsurface">Swipe Me</div>

Inside touchend, we check that the dist traveled from touchstart to touchend is a positive number above the specified threshold value (ie: 150), since in a right swipe, that dist should always be positive based on the equation used (versus negative for a left swipe). At the same time, we make sure any vertical lateral movement traveled is less than 100px to weed out diagonal swipes. Since the vertical movement can occur either above the starting touch point or below, we use Math.abs() when getting the absolute vertical dist traveled so both scenarios are covered when comparing it to our vertical threshold value of 100.

A generic swipe detecting function

Now that we got right swipe down, lets create a more generic function that detects swiping in either of the 4 directions (left, right, up, or down):

function swipedetect(el, callback){
 
	var touchsurface = el,
	swipedir,
	startX,
	startY,
	distX,
	distY,
	threshold = 150, //required min distance traveled to be considered swipe
	restraint = 100, // maximum distance allowed at the same time in perpendicular direction
	allowedTime = 300, // maximum time allowed to travel that distance
	elapsedTime,
	startTime,
	handleswipe = callback || function(swipedir){}
 
	touchsurface.addEventListener('touchstart', function(e){
		var touchobj = e.changedTouches[0]
		swipedir = 'none'
		dist = 0
		startX = touchobj.pageX
		startY = touchobj.pageY
		startTime = new Date().getTime() // record time when finger first makes contact with surface
		e.preventDefault()
 	}, false)
 
	touchsurface.addEventListener('touchmove', function(e){
		e.preventDefault() // prevent scrolling when inside DIV
	}, false)
 
	touchsurface.addEventListener('touchend', function(e){
		var touchobj = e.changedTouches[0]
		distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface
		distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface
		elapsedTime = new Date().getTime() - startTime // get time elapsed
		if (elapsedTime <= allowedTime){ // first condition for awipe met
			if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
				swipedir = (distX < 0)? 'left' : 'right' // if dist traveled is negative, it indicates left swipe
			}
			else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
				swipedir = (distY < 0)? 'up' : 'down' // if dist traveled is negative, it indicates up swipe
			}
		}
		handleswipe(swipedir)
		e.preventDefault()
	}, false)
}
 
//USAGE:
/*
var el = document.getElementById('someel')
swipedetect(el, function(swipedir){
	swipedir contains either "none", "left", "right", "top", or "down"
	if (swipedir =='left')
		alert('You just swiped left!')
})
*/

swipedetect() accepts two parameters, the element to bind the touch events to, plus a function to execute when a swipe has occurred. The function parameter "swipedir" tells you the type of swipe that was just made with five possible values: "none", "left", "right", "top", or "down".

The below uses the swipedetect() function to show a "left", "right", "top", or "down" background image (overlaid on top of a default background image) depending on the swipe that has just occurred:

Example (mouse simulation added for non touch devices):

Swipe Me

The code used is:

window.addEventListener('load', function(){
	var el = document.getElementById('touchsurface2')
	var inner = document.getElementById('inner')
	var hidetimer = null
	swipedetect(el, function(swipedir){
		if (swipedir != 'none'){
			clearTimeout(hidetimer)
			var bgimage = swipedir + 'arrow.png' // naming convention is "leftarrow.png", "rightarrow.png" etc
			inner.style.background = 'transparent url(' + bgimage + ') center center no-repeat'
			hidetimer = setTimeout(function(){ // reset background image after 1 second
				inner.style.background = ''
			}, 1000)
		}
	})
}, false)

The HTML markup is:

<div id="touchsurface2">
	<div id="inner">
		Swipe Me
	</div>
</div>

We bind swipedetect() to "#touchsurface2", and whenever a valid swipe has occurred inside it, we change the "#inner" DIV's background image accordingly to reflect the type of swipe that has just occurred.

Monitoring touch actions at every stage, swipe image gallery