Slider Touch Interactivity

Chapter 11 11 mins

Learning outcomes:

  1. Detecting swipe gestures
  2. Setting up the logic for touch interaction

Introduction

Amongst the most difficult concepts involved in building a slider powered by JavaScript is giving touch interaction to it i.e. enabling it to respond to swipe gestures.

Given the browser compatibility tables for touch events, the ease of understanding their functionality, coupled with ideas such as detecting the swipe speed and swipe direction, it turns out that giving touch interaction to a slider can be a daunting task, at least for beginners.

Experienced programmers out there with solid CSS and JavaScript skills, especially in JavaScript Touch Events, however, might not have that of a difficult time in implementing touch-interactive sliders.

In this chapter, we aim to figure out how to implement a touch-interactive slider by listening to touch events on our slider and determining whether a given gesture is a swipe, whereby we perform a navigation if it is.

So, let's get going.

Detecting a swipe

Before we can turn ourselves to the glyphs of code, we first ought to understand what exactly is a swipe and how to detect it using JavaScript.

This topic has been very nicely covered in our JavaScript Touch Events — Basics chapter, specifically in the section Swipe gestures.

We recommend that if you're unfamiliar with touch events in JavaScript, then you must read the whole chapter in order to be well-prepared to follow along the code snippet of this chapter. The swipe detection code that we'll use here will be quite similar to the one shown in the link above.

So assuming that at this point, you have a profound knowledge of working with touch events and detecting swipe gestures in JavaScript, it's time to start implementing a touch-interactive slider.

Note that at this stage, we don't want to build a highly touch-interactive slider, one where as we move our finger across the touch screen, the slider moves with it. That requires even more rigorous coding, which we'll surely accomplish in the next chapter.

For now, we want to keep everything superbly simple. That is, we just want to build a slider than can detect a swipe gesture when we end a touch interaction with the screen and then perform a navigation based on the outcome of the check.

The first step in doing so is to listen to the touchstart and touchend events on the .slider_slides-cont_wrapper element.

We choose .slider_slides-cont_wrapper as the target of the events because it has already been selected in the slidesContWrapperElement variable in our code — otherwise, we could've also gone with .slider_slides-cont or even .slider (at least in this case).

In the code below, we accomplish this idea:

/* ... */
function navigateSlider() { /* ... */}


slidesContWrapperElement.addEventListener('touchstart', function(e) {});
slidesContWrapperElement.addEventListener('touchend', function(e) {}); /* ... */

Both the touch handlers are set up right after the navigateSlider() function's definition. Technically, they could be set anywhere in the code.

With the touch handlers set up, the next step is to fill them up with some code to detect a swipe gesture.

Essentially two things are needed in determining whether a touch gesture is a swipe or not. They are:

  1. The change in the x and y co-ordinates of the touch point between the beginning and end of the touch gesture.
  2. The time duration of the touch gesture.

To obtain each of these pieces of information, we need to start off right upon the touchstart event and determine their initial readings i.e. the x and y co-ordinates at that point and the time at that point.

This is done below:

var initialX, initialY, initialTime;

slidesContWrapperElement.addEventListener('touchstart', function(e) {
   initialX = e.touches[0].clientX;
   initialY = e.touches[0].clientY;
   initialTime = new Date();
});

As the names suggest, the initialX, initialY and initialTime variables hold the initial x co-ordinate, the initial y co-ordinate and the inital time, respectively, right when the touchstart event occurs.

The reason of making these variables global is simply because we'll need them later on in the touchend event's handler. If they were local variables, they would've disappeared as soon as the touchstart handler exited.

Alright, so this is it for the touchstart handler. The initial readings have been taken and now it's time to head over to the handler of touchend.

Upon the touchend event, we need to determine the change in all of the three readings taken above.

Let's get done with this first and then proceed with other concerns:

slidesContWrapperElement.addEventListener('touchend', function(e) {
   var deltaX = e.changedTouches[0].clientX - initialX;
   var deltaY = e.changedTouches[0].clientY - initialY;
   var deltaTime = new Date() - initialTime;
});

Once again, as the names suggest, the variables deltaX, deltaY and deltaTime represent the change in the x co-ordinate, the change in the y co-ordinate and the change in the time upon touchend, respectively.

The word 'delta' means 'change'.

With the changes recorded, now we are only left with processing them in order to determine whether they really constitute a swipe gesture or not.

But what exactly is our definition of a swipe gesture?

Well, a swipe gesture is a touch interaction whose absolute change in the x co-ordinate is greater than 40px, whose absolute change in the y co-ordinate is less than 150px, and whose change in time is less than 400ms.

Note that the values here are just randomly-chosen values that resonate well with our swipe detection code. If you want to, you can obviously scale them up or down.

For instance, instead of going with a time threshold of 400ms, you can scale this up to 400ms, 900ms or even 1s.

The definition given above simply translates to the following:

A swipe gesture is a touch interaction with Math.abs(deltaX) > 40, Math.abs(deltaY) < 150 and deltaTime < 400.

But this still doesn't help us in figuring out the direction of the swipe.

How to figure out the direction of the swipe?

Well, it's really simple: instead of applying Math.abs() on deltaX and then comparing the resulting absolute value with 40, we could directly compare deltaX with 40 or -40.

That is, if deltaX > 40, it means that the swipe was towards the right.

This is because in a swipe-right gesture, we start off from the left side of the touch surface with a lower clientX value and then end up at a higher clientX value on the right side. Subtracting a lower value from a higher value always gives a positive result.

Similarly, if deltaX < -40 it means that the swipe was towards the right.

Once again, this is because in a swipe-left gesture, we start off from the right side of the touch surface with a higher clientX value and then end up at a lower clientX value on the left side. Subtracting a higher value from a lower value always gives a negative result.

So to restate it:

A swipe-right gesture is a touch interaction with deltaX > 40, Math.abs(deltaY) < 150 and deltaTime < 400.

Similarly, a swipe-left gesture is a touch interaction with deltaX < -40, Math.abs(deltaY) < 150 and deltaTime < 400.

Let's take this idea into our touchend handler.

Consider the code below:

slidesContWrapperElement.addEventListener('touchend', function(e) {
   var deltaX = e.changedTouches[0].clientX - initialX;
   var deltaY = Math.abs(e.changedTouches[0].clientY - initialY);
   var deltaTime = new Date() - initialTime;

   if (deltaTime < 400 && deltaY < 150) {
      if (deltaX > 40) {
         // Swipe-right gesture.
      }
      else if (deltaX < -40) {
         // Swipe-left gesture.
      }
   }
});

The first if statement in line 6 makes sure that both the conditions for a swipe gesture (i.e. swipe-left or swipe-right) are met.

If they are indeed met, another set of conditionals, from lines 7 - 12, determines the direction of the swipe and makes sure that the change in the x co-ordinate is beyond the given threshold.

Simple.

We are nearing the end of this chapter with just one thing left — calling navigateSlider() function within the nested if conditionals above. That's what we do up next.

Final steps

Let's think for a moment what would we need to do if a given swipe gesture is detected.

First, for the swipe-right gesture.

A swipe-right gesture means that we begin at the left side on the touch surface and go towards the right. Visually, this also means that we are trying to slide in something from the left side.

Hence, a swipe-right gesture is the equivalent of pressing the ← Previous button, i.e. decrementing newIndex and then calling navigateSlider().

Now, let's consider the swipe-left gesture.

A swipe-left gesture means that we begin at the right side on the touch surface and go towards the left. Visually, this means that we are trying to slide in something from the right side.

Hence, a swipe-right gesture is the equivalent of pressing the Next → button, i.e. incrementing newIndex and then calling navigateSlider().

Summing up both these ideas, below we write the final statements in our touchend handler:

slidesContWrapperElement.addEventListener('touchend', function(e) {
   var deltaX = e.changedTouches[0].clientX - initialX;
   var deltaY = Math.abs(e.changedTouches[0].clientY - initialY);
   var deltaTime = new Date() - initialTime;

   if (deltaTime < 400 && deltaY < 150) {
      if (deltaX > 40) {
         // Swipe-right gesture.
newIndex--;
navigateSlider(); } else if (deltaX < -40) { // Swipe-left gesture.
newIndex++;
navigateSlider(); } } });

Time to try out the slider:

Live Example

Voila! We just made a touch-interactive slider.