How to Drag on a Touch Device

February 1, 2020

If you want interactive pages, then you'll probably need to handle various types of events, like mouse events, but mobile phones and iPads don't have mice. Here's how to make something draggable, as in "click-and-drag the mouse," on a touch device. To fully follow what is demonstrated, it will be helpful if you can open this page on a computer with a mouse, and on a touch device.

This is Broken on a Touch Device

Below is a small canvas with a square drawn inside. Click on the square with the mouse to move the square around within the canvas. Pretty exciting, I know.

Yes, the square is blurry, but that will be addressed on a different page. A non-blurry implementation, based on SVG, is given here .

If you try to drag with your finger on a touch device, this little experiment won't work because the canvas above is listening for the wrong kinds of events. You can read the underlying contents (the HTML and JavaScript) of this page to see every detail of the code, but the important point is that the canvas listens for only these events:

theCanvas.addEventListener("mousedown",doMouseDown,false);
theCanvas.addEventListener("mouseup",doMouseUp,false); 
theCanvas.addEventListener("mousemove",doMouseMove,false);
theCanvas.addEventListener("mouseout",doMouseOut,false);

What's happening with these events is this.

Anyone who's done much graphics programming has written code like this many (many!) times. To see all of the details, look at the page source. I'm not using any kind of content management system, so the source is human-readable, with comments.

Handling these mouse events works on a non-touch device, but they don't really make sense for a touch screen.

This Works on a Touch Device

The fix is pretty easy. Touch devices have three events that are analagous to the corresponding mouse events: touchstart, touchend and touchmove can be used in a way similar to mousedown, mouseup and mousemove.

To make the square draggable on both touch and mouse devices, add the three touch events to the four mouse events that are already being handled.

Here are the details of what was changed in the code. First, add these additional listeners.

    theCanvas.addEventListener("touchstart",doMouseDown,false);
    theCanvas.addEventListener("touchend",doMouseUp,false); 
    theCanvas.addEventListener("touchmove",doMouseMove,false);

The original event listeners, doMouseDown, etc., still work, with one modification. On touch devices, the listeners will receive touch events instead of mouse events, and touch events have a different type, with different fields. Whereas a mouse event has clientX and clientY fields for where the mouse was clicked, a touch event does not. Instead, a touch event holds an array, touches, that holds multiple sets of coordinates, one for each "touch" involved (i.e., multiple fingers).

Each time the listener code is called, it needs to access the mouse or finger location, and the two cases need to be distinguished. There are various ways this could be done, but one way is based on looking at what the event variable holds, like this.

   
if ('clientX' in theEvent)
    {
        // Must be a mouse event, so use theEvent.clientX and 
        // the Event.clientY.
    }
else
    {
        // Must be a touch event. We only care about the first 
        // finger/touch, so use theEvent.touches[0].clientX and 
        // theEvent.touches[0].clientY.
    }

Another issue is the fact that we don't want the browser to react to touches in the canvas – we are handling it. To prevent the browser from reacting to touches, the canvas needs two style settings:

    touch-action: none;
    user-select: none;

Setting touch-action: none means that the browser won't try to do anything like pan or zoom the canvas in response to touches, and user-select: none means that the browser won't allow the user to select the canvas (like for copy-and-paste).

Sharp-eyed readers might be wondering about the mouseout event. At one time, there was a touchleave event meant to be much like a mouseout event, but it was deprecated and (as far as I can tell) doesn't work on most browsers these days. If necessary, it's possible to replicate the functionality of mouseout/touchleave in the implementation of doMouseMove, but I haven't done so. If you play with the canvases above on a mouse device and on a touch device, you'll see that dragging stops if the mouse leaves the canvas, but it doesn't stop on a touch device. On a touch device, your finger can leave the canvas and come back to it, and the "drag" continues; that doesn't work with a mouse. The reason the two cases are different is because the doMouseOut code can never be called on a touch device.