Implementing Touch Gestures with JavaScript

Oct 01, 2015
Javascript, Design
~4min
Share on Twitter

Don’t be afraid to just get in there and write your own 🤓

Touch support is becoming more and more likely to be required in your applications. Let’s take a look at why and then how you can combat this with some simple script 👊

The Impact of Responsive Web Design permalink

Responsive web design is commonplace in the majority of projects these days. Developing web applications whilst considering an array of device sizes is the norm. In fact, many projects will prioritise mobile first.

Consider this scenario though. You’ve put the graft in and created a web app for your end client. It’s looking sweet across all your test devices. You’ve tapped about a bit and all seems good. You hand it over to the client…

Oh 😞, It’s not working. When I swipe, I expected this to do this, nothing happens… and I can’t rotate this?!

There can almost be a blurred definition of what going “responsive” entails. By definition, responsive design deals with the look and render of your site/app. But, a client or end user may be expecting an almost native experience for devices with touch support.

Native “look and feel” may be a little out of sight without reaching for something like React Native. But, with a little JavaScript you can add basic touch support to your web applications. All is not lost 😅

For those in camp TL;DR, here’s a demo!

Check out this pen by Jhey (@jh3y) on CodePen.


All About Events permalink

It’s of no surprise that we listen for certain events to put together basic touch support. Much like binding an event listener for mouse interaction, you bind an event listener for a touch.

For the most basic touch gestures such as a tap. The browser can deal with these without any changes; a tap is a click handled by the “click” event.

How about more complex gestures such as swiping and pinching? These make the concept of touch more complicated. The result is that we need more events to listen for and bind to.

  1. Start the touch — touchstart
  2. Listen for any movement during the touch to handle gestures — touchmove
  3. End the touch — touchend
  4. Abandon the touch — touchcancel/touchleave

A Basic Start permalink

Let’s dive into a simple example 🏊


            javascript
            
          const body = document.body
body.addEventListener('touchstart', () => body.style.background = 'red')
body.addEventListener('touchmove', () => body.style.background = 'blue')
body.addEventListener('touchend', () => body.style.background = 'green')

Here, we just change the body style when different touch events are fired 🎨

On touch start the body color will change to red, if we take our finger away, body color will change to green. If we start a touch, move our finger around, and then take it away, we would see red, then blue, then green.

You can see this happening in the demo.

Going Further, Swipe permalink

One of the most requested touch gestures is horizontal swiping. This is usually required for carousel or galleries.

What are the logical steps for this gesture? 🚶

  1. The user starts by placing their finger on the screen — touchstart
  2. The user moves their finger across the screen — touchmove
  3. The user lifts their finger from the screen ending the swipe — touchend

We could actually detect swipe detection in a very basic way by not using touchmove. Using the information provided in the touchend event we can determine the gesture.

This leads onto the anatomy of the TouchEvent.


A TouchEvent is much like any other event. But, we get extra information about how many touches there are and where. This information is critical to implementing touch gestures.

You can read more about the different properties of a TouchEvent here 📖


A Touch object gives you various information about a Touch such as its location on screen. See here for more.


For a basic horizontal swipe, we can make use of the touchendand touchstartevents. We only need the start and end location of our touch to determine the gesture.

Consider the following implementation;


            javascript
            
          const root = document.documentElement
const body = document.body
const header = document.querySelector('h1')
const swipeLimit = 50

let startX
const endTouch = e => {
  root.style.setProperty('--bg', 'limegreen')
  const finishingTouch = e.changedTouches[0].clientX
  if (startX < finishingTouch - swipeLimit) {
    header.textContent = 'Swiped Right'
  } else {
    header.textContent = 'Swiped Left'
  }
  root.style.setProperty('--translate', 0)
  body.removeEventListener('touchmove', moveTouch)
  body.removeEventListener('touchend', endTouch)
}
const moveTouch = e => {
  root.style.setProperty('--bg', 'dodgerblue')
  const progressX = startX - e.touches[0].clientX
  const translation = progressX > 0
    ? parseInt(-Math.abs(progressX))
    : parseInt(Math.abs(progressX))
  root.style.setProperty('--translate', translation)
}
const startTouch = e => {
  const { touches } = e
  if (touches && touches.length === 1) {
    const touch = touches[0]
    startX = touch.clientX
    root.style.setProperty('--bg', '#e74c3c')
    body.addEventListener('touchmove', moveTouch)
    body.addEventListener('touchend', endTouch)
  }
}
body.addEventListener('touchstart', startTouch)

There is a fair bit to take in there but we can break it down.

All we are doing is adding event listeners to the document.body. As the TouchEvents fire, we update some CSS variables and update some header content.

Starting with touchstart permalink


            javascript
            
          const startTouch = e => {
  const { touches } = e
  if (touches && touches.length === 1) {
    const touch = touches[0]
    startX = touch.clientX
    root.style.setProperty('--bg', '#e74c3c')
    body.addEventListener('touchmove', moveTouch)
    body.addEventListener('touchend', endTouch)
  }
}
  1. We check the amount of touches. We can assume a single finger swipe if touches.length === 1.
  2. If we have the right amount of touches, we store the start position with startX. This is the value of clientX on the Touch event.
  3. Bind to the touchmoveand touchendevents. We also update the CSS variable --bg to show a touchstart.

Moving with touchmove permalink

We aren’t using touchmove for anything important here. But we can give a little user feedback by updating some CSS variables for translation and background-color. This is certainly important when trying to provide a better experience for your users. For a swipe you might move the element as it’s being touched.


            javascript
            
          const moveTouch = e => {
  root.style.setProperty('--bg', 'dodgerblue')
  const progressX = startX - e.touches[0].clientX
  const translation = progressX > 0
    ? parseInt(-Math.abs(progressX))
    : parseInt(Math.abs(progressX))
  root.style.setProperty('--translate', translation)
}

We calculate the correct distance to translate our element by referencing the starting and current location of the touch. We work out whether to apply it in a negative manner or positive based on swipe direction.

Ending with touchend permalink


            javascript
            
          const endTouch = e => {
  root.style.setProperty('--bg', 'limegreen')
  const finishingTouch = e.changedTouches[0].clientX
  if (startX < finishingTouch - swipeLimit) {
    header.textContent = 'Swiped Right'
  } else {
    header.textContent = 'Swiped Left'
  }
  root.style.setProperty('--translate', 0)
  body.removeEventListener('touchmove', moveTouch)
  body.removeEventListener('touchend', endTouch)
}
  1. There will only be one solo touch within the changedTouches array of our TouchEvent. We grab the last location of our touch using the clientXproperty of this solo Touch.
  2. We can then use this value and compare it to the starting position of our touch in combination with a swipe limit. Why do we need a swipe limit? Without it, a user tapping the screen could trigger swipe logic. This is because their finger may have moved a mere pixel on the horizontal plane. For this implementation, we define a swipe limit of 50px. Based on this comparison we can determine whether the user has swiped left or right 🎉
  3. Carry out the action we want for swiping and then tidy up and remove event listeners from document.body.

The Rest is Down To You! (And Your Clients) permalink

We’ve taken a look at implementing basic touch support for a swipe gesture. No extra libraries or frameworks required 🎉 We could do a lot more with our solution but this is just an intro. Desired experience for your apps will likely differ from app to app.

On top of that we have only looked at one gesture. There are still gestures such as pinch and rotate to consider. These require the use of multi-touch detection and a little extra thinking. You can see a demo of pinch zooming below.

Check out this pen by Jhey (@jh3y) on CodePen.

Check out this pen by Jhey (@jh3y) on CodePen.