Wednesday 13 January 2016

Writing a touch enabled responsive slideshow in pure javascript

The issue at hand

I've kind of started to enjoy Javascript and even CSS as of lately, and decided to try to write a slideshow in pure Javascript, no frameworks or frills involved. We use slideshows all the time on different web sites, be it good or bad, but I'm always resorting to some kind of component or framework. And there's plenty of good ones out there, doing lovely stuff, but since I'm not that well versed in advanced Javascript, I can't really read the code and understand what's actually happening. 

Another issue is of course that these awesome frameworks are so generic and cover all possible options and devices out there, which in all honesty makes the code a bit bloated. And when we started to write our own code at work, I realised it can actually be done. I managed to get the JS-part down to 250 lines, yay! Demo is here. Code is found on Github.

What I wanted to solve

  • It should be written in pure Javascript, with no frameworks or mysteries.
  • It must be responsive.
  • It must handle touch events.
  • It must be possible to have several slideshows on the same page, when the slideshowmania hits.
  • It must be able to handle both looping and not looping slideshows.
  • And for fun, it must handle the people not using Javascript. Because you never know, Javascript might disappear, right?

What I didn't care about

It's not like this slideshow is going to take over the world, so I'm not too worried about:
  • Old browsers. Basically, if it works on my machine, a Macbook with OSX and Windows 10, with latest versions of Safari, Chrome, Firefox and Edge on it, I'm happy.
  • Testing on real life phones or tablets. If it works on my Nexus 6p with Chrome, I'm happy.
Which means there's room for improvement, to say the least. :)

Basic idea

The idea behind this slideshow is simple; an outer container holding a number of slides. The slides are vertically aligned, by setting them as inline-blocks and not wrapping whitespace on the outer container. View code at Codepen.

To remove the annoying 4 pixels between inline-blocks, there are a number of tricks. I set the font-size to 0 on the outer container, that works fine. That's basically it. Now we need some smart behaviour to turn this into a slideshow.

No javascript

This is easily handled with this markup. All we do is set the outer container to overflow-x: auto. This will make it horizontally scrollable. So if we have the class 'no-js' in an outer container, we change the CSS of the slideshow: View Codepen. Now we have the world's simplest slideshow, neat. :)

No touch - desktop browser

For desktop browsers, we'll make the outer container hide it's overflow and present buttons that will move the slides back and forward. There are a number of ways to do the scrolling, but I went with CSS transforms and transitions. View Codepen.

Touch - phones and tablets

For touch devices, we want to still present the buttons, but also make it possible to swipe between slides. For this, there are four events we have to listen to:
  • Touchstarted - a touch has been detected. We save the pixel position and timestamp.
  • Touchmove - finger is moving over the screen. We want the slide to move with the finger.
  • Touchend - touch ends. We want to check the position, move to next or previous slide or just stay where we are.
  • Touchcancel - touch moves outside area. We want to cancel the ongoing touch and set start position to null.
We'll use CSS transforms again to move the slide along with the finger, but this time without the transition. View Codepen.

For the touchend handler, we want to move to another slide if the touch has been really quick, like a flick, or if the touch has passed a certain percent of the slide. View Codepen.

Not much more to it, except handling touch details like cancelling vertical swiping and handling what to do when you've reached the end of the slides.

Looping

How to handle looping? Well, I've just added an extra first and last item to the DOM so when we loop we use the extra slides that are placed at the end of the array. Once the transition is done we do another transform without transition effect so the array of slides actually ends up at the "real" first and last item. Confusing? View the code on Github. :)

Feature checking without Modernizr

Modernizr is an excellent framework for detecting which features a browser has, but since I'm not using any frameworks we'll do it ourselves. In the HTML-file, I set a class in the HTML-tag named "no-js". Further down, I remove it using Javascript. If Javascript is not enabled, the class stays. Easy! For the touch/no-touch feature I use another small snippet of code. View Codepen.