Multi-Input JavaScript Support

I had the opportunity to attend this year’s Google I/O in San Francisco. Among the loads of great information I was able to learn and take away from the sessions, one of the biggest “Aha!” moments came from the session Point, Click, Tap, Touch: Building Multi-Device Web Interfaces. In it, the presenters called out the problem of developers disabling mouse support if touch support is found. Doing so may result in a frustrating user experience for users whose devices offer multi-input support (e.g. Microsoft Surface, Chromebook Pixel). In short, developers should not assume a user will use one type of input over another.

The solution to this problem is to build in support for any and all types of input to offer a better user experience, and at the same time, better future-proof our sites. This is easily accomplished by listening for both mouse and touch events, and have your JavaScript code behave accordingly, so you can offer support of any interaction type.

Inspired by this (and having obtained a new Chromebook Pixel which proved to be a wonderful device to test on), I created a simple drag-and-drop demo that binds all mouse and touch events (even for the MS Surface), even allowing for multiple items to be moved at the same time. I invite you to check it out, view its source, and play around with it. Hopefully it is something you can reference for your own projects to improve the user experience across the range of devices.

View Demo »

A few notes about the demo

  • All mouse and touch events are bound and handled appropriately.
  • Users can drag more than one object at a time.
  • On multi-input devices (Surface, Pixel), users can drag and drop using mouse and touch events at the same time.
  • The JS is optimized to reduce layout thrashing, a common issue with coordinate-based layouts that reference offsetWidth (causes a layout) and/or style.width (invalidates a layout). This was a tip I learned from another Google I/O session, Device Agnostic Development.
  • The rendering methods are performed in a render method, which is called via window.requestAnimFrame, rather than directly in the mouse/touch move handler. This further helps the JS optimization and keeps a more consistent frame rate.
  • The JS is problem-free and has been verified at JSHint.com.

Until next time, happy coding!

3 Responses to this post:

  • Thomas Giles says:

    Pretty cool, man. I haven’t got a touch+mouse input device myself, but I always make sure to allow both where possible for anyone that may use my stuff in the future.

    One minor issue I’m seeing with the drag behaviour is that if you move the mouse too fast while dragging, it gets left behind. Also, if you release the mouse button when in this “broken” state, the dragged object still thinks it’s being dragged, so when you move your mouse over it again, it starts to be dragged when it’s not supposed to. Also, by dragging the object out of the window, the object stops following.

    The way I’ve found that fixes this problem is to attach the “move” and “up” listeners to the window instead. This lets the user drag as fast as they like, and even continue their drag outside the edges of the window, and everything works just fine. This may add a little more complexity to whatever code handles all the dragging, but it would make this demo silky smooth! ;P

  • Great point, Thomas. I knew there was the issue of “moving too fast” with the mouse, but didn’t have a great solution for it since I used multiple event handlers, not just one. I’ll definitely noodle around with it some more and see if I can’t get some improvements in place. Cheers!

  • I’ve updated the demo with the suggested window listeners for mousemove/mouseup, which improves the original issue of losing track of a box when moving the mouse too fast. Just a few conditional checks and a variable to keep track of the object that the mouse selected and things are moving much more smoothly. Thanks for the tip, Thomas!

Leave a Comment