Before and After Image Viewer Part 3a: Supporting Touch Mobile Devices

Published Jul 3, 2023
Updated Jul 7, 2023
By Simon

Please note that this article is part of a whole and as such the code here may not be the best way of doing this or the most optimized. The idea of this article series is to go through the process that I used to make this image reveal hide viewer and introduce various features of CSS and JavaScript in a practical use case that you can use in other ways.

Note: It has since come to my attention that Android devices deal with hover slightly differently. So for that reason what I write about here is specific to iOS touch devices but does lead to a holistic solution.

In part one, I wrote how I am going to deal with mobile and desktop functionality. I decided that it was best to split the functions that tracked the position of the slider to work using actual pixel position for anything under 1000px and for anything over 1000px leave it using per cent. The work for this is done in the last part, parts 2 a and b, so let's use what we already have.

The 1000px number is somewhat arbitrary, it is based on the maximum image width and doesn't perfectly match keyboard/mouse and touch device sizes. Nothing really could these days. There is cross-over as a touch device could be larger than 1000px or a user may have their browser less than 1000px on a desktop. This highlights that you'll never get anything perfect on your first go and it is by working and understanding the problem and then building something that the true specification will be realised.

Okay, pep talk out of the way, let's do this next step.

What I will cover in this part:

  • Touch events.
  • Hover pseudo-class on mobile.
  • Media queries for screen density.
  • User Experience UX.

We also use various JavaScript methods and APIs.

For a final working example, please check Codepen.
https://codepen.io/simonramsay/pen/mdROdEo
Please note this example is slightly different from the code discussed here.

If you don't already have the code, grab the repo from Github.

You can grab the whole repo from GitHub.
https://github.com/siramsay/image-hide-reveal

How do you do this? Check out the Git going section on this site and working with remote repositories.

Check out the make-responsive branch.

Create a new branch from the make-responsive branch using the below command.

git checkout -b my-new-branch-name

Good, so with the code and a new branch, we can continue looking at a basic touch event.

If you check what we have so far on a touch device you will see not much happens. All that happens is when you touch the image viewer the handle used for adjusting the position of it appears but then doesn't disappear on touch end (or as I have since learnt it may disappear on Android). Let's fix this.

Adding Touch interactions to your code

Important! This section does not use the recommended way to set up touch. However, through the trial and error of what is written here, you learn about how hover works on mobile, specifically iOS and how we need to use media queries in CSS to target touch screens, both Android & iOS. This section is a slight detour but it was the path I took so I have left it here as it is useful information.

When working on this I set up the touch event somewhat incorrectly. I will look at setting up touch in the most robust and recommended way when we re-factor in Part 4. However, for now, let's go with it as it does work and we can learn something. The way I set it up seemed easy at the time as I copied the mouse move code and then wrapped it in a comparable touch event.

As pointed out, the only thing that happens when touching the viewer is the handle appears but none of the mouse events are triggered. The appearance of the handle is triggered by the hover pseudo-class in CSS set on the outer element. Namely the below code.

.outer:hover #handle {
  display: block;
}

To get the same functionality as the mouse events we need to set up some touch events.

Setting up a Touch Event

First, add an addEventListner with the touch move event and function. We add this to the handle in the same way as the mouse event listener. I add a console log inside.

handle.addEventListener('touchmove', function (e) {
 console.log('Touch Move')
})

Test the code

If you test this on your mobile device, preferably using a web inspector, you will see that when you touch the screen the hover is triggered and not the touch event; you will see the green handle div appear, but the console does not log the message.

The reason for this is the hover is set on the outer div to show the handle for desktop, the handle that the event listener is set on is not actually visible.

The difference on iOS is the hover remains triggered; the handle remains visible. On Android, the handle will disappear when you remove your finger.

This usually isn't an issue on mobile as hover is mostly added to links and you will click or touch a link and be taken to another page. However, in this instance, we are not taken to another page.

Once the handle is active and you touch it, you will see the console log message in the web inspector.

Let's note that you need to touch the screen first to make the touch event active. This is more because the handle is set to display none on load using CSS and we use hover to make it appear. This is not a bad thing and we will work with it. Good, so now we know that this works let's continue with our little exploration.

But first I will commit.
Commit: [basic-touch-with-hover f82ad4f] add touchmove wrapper event handler

Adding the code for touch

The rest of the code will pretty much be the same as the mouse-down function with the exception that we use touch events. So with that in mind let's do this next step.

  1. Copy the code from the mouse-down event into the touch event.

I copied the code and then committed so that we can see the changes we will make in the next part, namely switching out the mouse events and adding touch-specific properties.
Commit: [basic-touch-with-hover e36a408] copy mousedown inner code to touchmove

  1. Replace all the mouse events with a touch event. We can also remove the mouse-up listeners for now; handle.mouseup =  and window.addEventListener('mouseup', function (event) {})
   var windowWidth = document.body.clientWidth;
-  handle.onmouseup = function (e) {
-    trackerElement.removeEventListener('mousemove', trackMousePosition);
-  };
-  window.addEventListener('mouseup', function (event) {
-    trackerElement.removeEventListener('mousemove', trackMousePosition);
-  })
 
-  trackerElement.addEventListener('mousemove', trackMousePosition);
+  trackerElement.addEventListener('touchmove', trackMousePosition);
 
   function trackMousePosition(theEvent) {
 
-    const xPos = theEvent.clientX;
+    const xTouchPos = theEvent.clientX || theEvent.targetTouches[0].pageX;
 
     const trackerDOMRect = trackerElement.getBoundingClientRect();
 
     let mousePointerPos;
-    mousePointerPos = theEvent.clientX - trackerDOMRect.left;
+    mousePointerPos = xTouchPos - trackerDOMRect.x;
 
     //Check window width
     if (windowWidth <= maxWidth) {
 
-      if (xPos <= windowWidth) {
-        topImage.style.width = xPos + "px";
+      if (xTouchPos <= windowWidth) {
+        topImage.style.width = xTouchPos + "px";
       } else {
         topImage.style.width = windowWidth;
       }

You will note that I haven't changed the name of the trackMousePosition function that is passed into the inner touchmove event. I have done this more to illustrate that you don't need to as the scope of the function is contained in the outer touchmove block.

Do note that I have changed the var xPos to a new variable xTouchPos and I am setting it to the e.targetTouches[0].pageX value or the e.clientX value.

You'll need to do the same so make sure you change all usage of xPos to xTouchPos.

This is great, if you check this on your mobile it works!

You will, however, notice the handle remains when you stop touching the screen. Depending on your design this may not be an issue but In the next step, we will look at how we can disable this.

Commit: [basic-touch-with-hover cd52667] switch mouse events and event properties to touch

Thanks for reading, I hope you learnt something. In Part 3b, we get our prototype ready by exploring touch interactivity on both iOS and Android and then review the work for a full refactor.

Be sure to sign up for the newsletter to be the first to know. I write about front-end development and design.

Until next time, seize the day!