Skip to main content

Converting single-touch events to mouse events

Many times, there are various interactions that take place in a web app based on mouse movement, clicking, dragging, etc. With a mouse, this is fairly simple, as there is a single point, and events like mousedown, mouseup, and mousemove can be used to discover the state of the mouse at any given time.

On mobile browsers, however, it's a different story. Most phones support at least some kind of multi-touch, and the mousedown and mouseup events are not fired (there is, after all, no button). In addition, mousemove will only be applicable when dragging. Instead, mobile browsers dispatch touchstart, touchmove, and touchend events. Moreover, these events keep track of all touch points, not just the first or last one, and as such make handling them a bit more difficult.

For the purposes of an application that only requires tracking clicks and drags, it is more convenient to translate these touch events into mouse events without having code duplication. This can be accomplished quickly in several steps. First, we want to ignore events with more than one touch point, as this is used by many mobile browsers to zoom and/or scroll. Then, we want to get the information from the touch event and simulate a mouse event using that data. And finally, we dispatch the simulated event and prevent the default response to the touch event.

In code, it looks like this (replace element with whatever element you want the listener on):

var touchToMouse = function(event) {
    if (event.touches.length > 1) return; //allow default multi-touch gestures to work
    var touch = event.changedTouches[0];
    var type = "";
    
    switch (event.type) {
    case "touchstart": 
        type = "mousedown"; break;
    case "touchmove":  
        type="mousemove";   break;
    case "touchend":   
        type="mouseup";     break;
    default: 
        return;
    }
    
    // https://developer.mozilla.org/en/DOM/event.initMouseEvent for API
    var simulatedEvent = document.createEvent("MouseEvent");
    simulatedEvent.initMouseEvent(type, true, true, window, 1, 
            touch.screenX, touch.screenY, 
            touch.clientX, touch.clientY, false, 
            false, false, false, 0, null);
    
    touch.target.dispatchEvent(simulatedEvent);
    event.preventDefault();
};
element.ontouchstart = touchToMouse;
element.ontouchmove = touchToMouse;
element.ontouchend = touchToMouse;

And a closure-compiled version:

var touchToMouse=function(b){if(!(b.touches.length>1)){var a=b.changedTouches[0],c="";switch(b.type){case "touchstart":c="mousedown";break;case "touchmove":c="mousemove";break;case "touchend":c="mouseup";break;default:return}var d=document.createEvent("MouseEvent");d.initMouseEvent(c,true,true,window,1,a.screenX,a.screenY,a.clientX,a.clientY,false,false,false,false,0,null);a.target.dispatchEvent(d);b.preventDefault()}};
element.ontouchstart=touchToMouse;
element.ontouchmove=touchToMouse;
element.ontouchend=touchToMouse;

I hope you find this useful in any mobile web apps you're making. However, this will only track one touch point, so if you require more control over multi-touch features, I recommend checking out this guide. Though the title suggests that this is iPhone-only, it also applies to the default browser on Android, as it fires the same touch events.

Comments

  1. I've had success with this code, except when the drawing canvas is in a page loaded in a iFrame. Touch events are not translated into mouse events. Any ideas?

    ReplyDelete

Post a Comment

Popular posts from this blog

Linux on XPS 15 9550/9560 with TB16 Dock [Update:3/29]

Finally got a laptop to replace my fat tower at work - Dell XPS 15 9560. I was allowed to choose which one I wanted and chose the XPS for its Linux support since Dell ships developer edition XPS's running Ubuntu so I figured Linux support would be better than other manufacturers. At first they got me the model with the 4K screen but my monitors are 2K and multi-dpi support in Linux is virtually non-existent and even hi-dpi support on its own is pretty terrible. So I got it exchanged for the model with the regular 1080p screen (which happened to also be the updated 9560 model), which works much better. I'm very glad to report that pretty much everything works, including the TB16 desktop dock, with just a bit of settings tweaking. This post is to help anybody considering getting this setup or looking for help getting things working. For now, I am running Kubuntu 16.04 with KDE Neon installed. List of things I explicitly tested and work: WiFi, Bluetooth Thunderbolt charging

Broadcom Bluetooth Driver: Installer or Virus??

My primary computer at the moment is an HP dv6 laptop (with Broadcom 2070 Bluetooth chip). Upon getting my laptop, I immediately wiped the hard drive and slapped on a fresh copy of Windows 7. Installed the drivers from HP, and everything was rolling along nicely... Fast-forward 4 months... Now I'm having some issues with Bluetooth communication between my phone (Android) and my laptop. So I reinstall the Bluetooth driver... The installation took a long  time, but eventually errored-out, and quit. Of course, I had left the computer unattended during this, and came back to an almost-empty hard drive (!!!!). Restored for backup, some minor data loss, no problem... I thought I had snagged a virus in the download (even though it was direct from HP's site). Redownload, scan with multiple scanners, run it ....... SAME RESULT!!! Giving up on the official installer, I just took the drivers that the installer unpacked, and manually installed them... That didn't solve my original pr

JComboBox with Disabled Items

Recently, I was working on a project in Java and needed to have a combo box, but with certain items in the list disabled (e.g. gray and non-selectable). At first, I simply set a custom renderer for the combo box which checked if the item was disabled. That, however, did not prevent the items from being selected. Thus, I set about to find a viable solution. There are plenty of solutions out there, but none seemed to work exactly the way I wanted. In the end, I ended up subclassing JComboBox to provide the functionality of disabling individual items. Here is my result, in under 100 lines: import java.awt.Component; import java.util.ArrayList; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.plaf.basic.BasicComboBoxRenderer; public class PartialDisableComboBox extends JComboBox { private static final long serialVersionUID = -1690671707274328126L; private ArrayList<boolean> itemsState = new ArrayList<boolean>(); public PartialDisableComboBox