I'm developing a webapp (not a website with pages of interesting text) with a very different interface for touch (your finger hides the screen when you click) and mouse (relies heavily on hover preview). How can I detect that my user has no mouse to present him the right interface? I plan to leave a switch for people with both mouse and touch (like some notebooks).
The touch event capability in the browser doesn't actually mean the user is using a touch device (for example, Modernizr doesn't cut it). The code that correctly answers the question should return false if the device has a mouse, true otherwise. For devices with mouse and touch, it should return false (not touch only)
As a side note, my touch interface might also be suitable for keyboard-only devices, so it's more the lack of mouse I'm looking to detect.
To make the need more clear, here is the API that I'm looking to implement:
// Level 1
// The current answers provide a way to do that.
hasTouch();
// Returns true if a mouse is expected.
// Note: as explained by the OP, this is not !hasTouch()
// I don't think we have this in the answers already, that why I offer a bounty
hasMouse();
// Level 2 (I don't think it's possible, but maybe I'm wrong, so why not asking)
// callback is called when the result of "hasTouch()" changes.
listenHasTouchChanges(callback);
// callback is called when the result of "hasMouse()" changes.
listenHasMouseChanges(callback);
The main trouble is that you have the following different classes of devices/use cases:
What's worse, is that one can transition from some of these classes to others (plugs in a mouse, connects to keyboard), or a user may APPEAR to be on a normal laptop until they reach out and touch the screen.
You are correct in assuming that the presence of event constructors in the browser is not a good way to move forward (and it is somewhat inconsistent). Additionally, unless you are tracking a very specific event or only trying to rule out a few classes above, using events themselves isn't full proof.
For example, say you've discovered that a user have emitted a real mousemove (not the false one from touch events, see http://www.html5rocks.com/en/mobile/touchandmouse/).
Then what?
You enable hover styles? You add more buttons?
Either way you are increasing time to glass because you have to wait for an event to fire.
But then what happens when your noble user decides wants to unplug his mouse and go full touch.. do you wait for him to touch your now crammed interface, then change it right after he's made the effort to pinpoint your now crowded UI?
In bullet form, quoting stucox at https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101
- We want to detect the presence of a mouse
- Ae probably can't detect before an event is fired
- As such, what we're detecting is if a mouse has been used in this session — it won't be immediately from page load
- We probably also can't detect that there isn't a mouse — it'd be undefined until true (I think this makes more sense than setting it false until proven)
- And we probably can't detect if a mouse is disconnected mid-session — that'll be indistinguishable from the user just giving up with their mouse
An aside: the browser DOES know when a user plugs in a mouse/connects to a keyboard, but doesn't expose it to JavaScript.. dang!
This should lead you to the following:
Tracking the current capabilities of a given user is complex, unreliable, and of dubious merit
The idea of progressive enhancement applies quite well here, though. Build an experience that works smoothly no matter the context of the user. Then make assumptions based on browser features/media queries to add functionality that will be relative in the assumed context. Presence of a mouse is just one of the multitudes of ways in which different users on different devices experience your website. Create something with merit at its kernel and don't worry too much about how people click the buttons.