Detecting that the browser has no mouse and is touch-only
Solution 1
As of 2018 there is a good and reliable way to detect if a browser has a mouse (or similar input device): CSS4 media interaction features which are now supported by almost any modern browser (except IE 11 and special mobile browsers).
W3C:
The pointer media feature is used to query the presence and accuracy of a pointing device such as a mouse.
See the following options:
/* The primary input mechanism of the device includes a
pointing device of limited accuracy. */
@media (pointer: coarse) { ... }
/* The primary input mechanism of the device
includes an accurate pointing device. */
@media (pointer: fine) { ... }
/* The primary input mechanism of the
device does not include a pointing device. */
@media (pointer: none) { ... }
/* Primary input mechanism system can
hover over elements with ease */
@media (hover: hover) { ... }
/* Primary input mechanism cannot hover
at all or cannot conveniently hover
(e.g., many mobile devices emulate hovering
when the user performs an inconvenient long tap),
or there is no primary pointing input mechanism */
@media (hover: none) { ... }
/* One or more available input mechanism(s)
can hover over elements with ease */
@media (any-hover: hover) { ... }
/* One or more available input mechanism(s) cannot
hover (or there are no pointing input mechanisms) */
@media (any-hover: none) { ... }
Media queries can also be used in JS:
if(window.matchMedia("(any-hover: none)").matches) {
// do something
}
Related:
W3 documentation: https://www.w3.org/TR/mediaqueries-4/#mf-interaction
Browser support: https://caniuse.com/#search=media%20features
Similar problem: Detect if a client device supports :hover and :focus states
Solution 2
The main trouble is that you have the following different classes of devices/use cases:
- Mouse and keyboard (desktop)
- Touch only (phone/tablet)
- Mouse, keyboard, and touch (touch laptops)
- Touch and keyboard (bluetooth keyboard on tablet)
- Mouse only (Disabled user/browsing preference)
- Keyboard only (Disabled user/browsing preference)
- Touch and mouse (ie hover events from Galaxy Note 2 pen)
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 has 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
- We probably can't detect it 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.
Solution 3
How about listening for a mousemove event on the document. Then until you hear that event you assume that the device is touch or keyboard only.
var mouseDetected = false;
function onMouseMove(e) {
unlisten('mousemove', onMouseMove, false);
mouseDetected = true;
// initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);
(Where listen
and unlisten
delegate to addEventListener
or attachEvent
as appropriate.)
Hopefully this wouldn't lead to too much visual jank, it would suck if you need massive re-layouts based on mode...
Solution 4
@Wyatt's answer is great and gives us a lot to think about.
On my case, I chose to listen for the first interaction, to only then set a behavior. So, even if the user has a mouse, I will treat as touch device if first interaction was a touch.
Considering the given order in which events are processed:
- touchstart
- touchmove
- touchend
- mouseover
- mousemove
- mousedown
- mouseup
- click
We can assume that if mouse event gets triggered before touch, it is a real mouse event, not an emulated one. Example (using jQuery):
$(document).ready(function() {
var $body = $('body');
var detectMouse = function(e){
if (e.type === 'mousedown') {
alert('Mouse interaction!');
}
else if (e.type === 'touchstart') {
alert('Touch interaction!');
}
// remove event bindings, so it only runs once
$body.off('mousedown touchstart', detectMouse);
}
// attach both events to body
$body.on('mousedown touchstart', detectMouse);
});
That worked for me
Solution 5
It's only possible to detect if a browser is touch capable. There is no way to know if it actually has a touch screen or a mouse connected.
One can prioritize the use though by listening to touch event instead of mouse event if touch capability is detected.
To detect touch capability cross-browser:
function hasTouch() {
return (('ontouchstart' in window) || // html5 browsers
(navigator.maxTouchPoints > 0) || // future IE
(navigator.msMaxTouchPoints > 0)); // current IE10
}
Then one can use this to check:
if (!hasTouch()) alert('Sorry, need touch!);
or to choose which event to listen to, either:
var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);
or use separate approaches for touch vs. non-touch:
if (hasTouch() === true) {
someElement.addEventListener('touchend' , touchHandler, false);
} else {
someElement.addEventListener('click' , mouseHandler, false);
}
function touchHandler(e) {
/// stop event somehow
e.stopPropagation();
e.preventDefault();
window.event.cancelBubble = true;
// ...
return false; // :-)
}
function mouseHandler(e) {
// sorry, touch only - or - do something useful and non-restrictive for user
}
For mouse one can only detect if the mouse is being used, not if it exists or not. One can setup a global flag to indicate that mouse was detected by usage (similar to an existing answer, but simplified a bit):
var hasMouse = false;
window.onmousemove = function() {
hasMouse = true;
}
(one cannot include mouseup
or mousedown
as these events can also be triggered by touch)
Browsers restricts access to low-level system APIs which is needed to be able to detect features such as hardware capabilities of the system it's being used on.
There is the possibility to perhaps write a plugin/extension to access these but via JavaScript and DOM such detection is limited for this purpose and one would have to write a plugin specific for the various OS platforms.
So in conclusion: such detection can only be estimated by a "good guess".
Related videos on Youtube
Comments
-
nraynaud about 2 years
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);
-
Kevin Reid about 12 yearsRelated question: Detect whether browser has keyboard/arrow keys in web page
-
davethegr8 about 12 yearsI think you need to rethink your design if you want one app to be applicable to both desktop and mobile/touch but have different behaviors for each. I don't think what you're after is actually possible at this point, since a quick search on Google for "javascript detect mouse" shows one moderately useful post on quirksmode.org for detecting various states of the mouse (clicks, position, etc), but ZERO results on whether or not the mouse actually exists.
-
nraynaud about 12 yearsMaybe that's because Google didn't help that I asked it here.
-
Parag Gajjar about 12 yearsHave you tried document mouseenter from jquery? $(document).mouseenter(function(e) { alert("mouse"); });
-
Abhishek almost 11 yearsLate to the party, but I'll throw in here that you always have to keep in mind the many devices nowadays that support both touch AND mouse
-
nraynaud almost 11 yearsmy question was quite precisely worded about having no mouse, as in "no mouse at all". It was at the time to present the right interface to the right people by default. for an app that could be used on the field feet in the mud, in a moving vehicle or at the office on a PC.
-
Abhishek almost 11 yearsJust a note for posterity - if you're looking to do the opposite (detect if mouse is available) stackoverflow.com/a/16423486/5056
-
Jordan Gray over 10 yearsAfter considering nearly a dozen promising avenues only to reject each one within minutes, this question is driving me quite splendidly bonkers.
-
-
nraynaud over 12 yearsI don't really know what to get from your post, but if ('onmouseover' in $('body')[0]) alert('onmouseover'); displays a message in iPhone too
-
Jon Gjengset about 12 yearsThis only checks if the mouseover function is defined, which it would be on almost all browsers. It does not detect if a mouse is actually present.
-
Jon Gjengset about 12 yearsIt's a good idea, but unfortunately the delay in response will make it unusable when the UI of the application depends on whether a mouse is available.. This is especially true if the application may be iframed, so mouse events will only hit it if the mouse moves over the iframe itself..
-
Dan about 12 yearsYep, I can't think of another way then, without doing browser sniffing or testing screen resolution and inferring the likely device capabilities.
-
nraynaud about 12 yearsthat's a creative idea! it's a bit more complicated than that because of synthesized mouse events in touch browser, but that's an idea!
-
Jon Gjengset about 12 yearsThis won't work for devices that may or may not have touch screens and a mouse. For instance, a desktop Windows computer may be connected to a touch screen, but will usually also have a mouse, whereas a tablet may also be running Windows, but may not have a mouse connected..
-
Gigi about 12 years@Jonhoo Just assume that Desktop operating systems have a mouse attached. After all, they must support a wide range of software that was not developed with a touchscreen in mind.
-
Jon Gjengset about 12 yearsThat is precisely what I'm trying to do. I'm trying to create an interface that will work in the best possible way on both tablets (no mouse) and with a mouse, but those interfaces are necessarily very different.
-
Jon Gjengset about 12 yearsWhat about tablets running plain Windows 8? Or Linux? Or laptops running Android?
-
Gigi about 12 years@Jonhoo Obviously this approach is less than optimal, but there is no portable way to know that (yet). If one is running a laptop with Android, just assume it is touch-capable. If one is running a Windows8 tablet, just assume it is mouse-capable (the OS must emulate the mouse for non-touch programs).
-
Mamey about 12 yearsThis could work if the application starts with a splash screen and a "continue" button. If the mouse moves before the first mousedown event then you have a mouse. It would only fail if the button loaded directly under the mouse and the user has a very steady hand (even moving 1 pixel should be picked up).
-
Jon Gjengset about 12 yearsIt's actually a pretty good suggestion, but it delays the time before the user gets to the real interface. Also, I'll have to provide a way of switching after the initial choice. Ends up being more work than if it could simply be detected..
-
Teemu Ikonen about 12 yearsI agree with broady. You're best of by using a device detection (like DeviceAtlas) and select offered interface at load time.
-
Jon Gjengset about 12 yearsBecause some browsers (IE9 for instance) report that the function exists even if it will never be triggered. I believe this is also the "correct" behavior.
-
Peter Burns over 11 yearsnice idea, but does not appear to work in our testing. iPads trigger this event.
-
Michael Haren about 11 years@JeffAtwood what did you end up doing in your case?
-
GameAlchemist almost 11 yearsTo know wether it is a mouse or a touch device you must also Hook the touch event and prevent its default handler. So the first Who Wins is the device you seek.
-
daredev over 10 years@JeffAtwood @MichaelHaren this prevents the event from firing (see my answer):
window.document.body.addEventListener('mousemove', function (e) { e.preventDefault(); e.stopImmediatePropagation(); }, true);
-
Samuel Rossille over 10 yearsThanks for there good workaround suggestions... I think the main problem not being sovled I'll have to resort to one of these
-
T4NK3R over 10 yearsAsking the user is clearly the best way - if not always foolproof - And gives you a convenient place to put up upgrade-notifications and what not. I think you're over thinking the "problem"..
-
Chris Gunawardena over 10 yearsUnfortunately mousemove gets triggered on click on a ipad. Only tested with simulator. For hasMouse() I was using if( !('ontouchstart' in window) ) return true; but doesn't work for touch supported laptops.
-
vsync about 10 yearsSafari ipad returns
true
for'onmousedown' in window
-
Peter Wooster over 9 yearsiPads definitely trigger the mousemove event, right before the mousedown event. I've found that mousedown count > 0 and mousedown count == mousemove count to be a good way to detect no mouse. I can't duplicate this with a real mouse.
-
netzaffin about 9 yearsDoesn't work for me, Ipad Safari (IOS8.3) also detects a mouse with this snippet
-
Hugo Silva almost 9 years@netzaffin. Thanks for the feedback, I found it to be more consistent using mousedown instead of mouseover. Would you have a look at this fiddle from your IOS and let me know the outcome? Cheers jsfiddle.net/bkwb0qen/15/embedded/result
-
Jordan Gray almost 9 yearsSpecifically,
any-pointer
andany-hover
will let you investigate all applicable device capabilities. Nice to get a peek at how we might solve this problem in the future! :) -
suncat100 over 8 yearsJust detect a touchstart event to kill the mousemove event listener. That way, the mousemove eventlistener will never trigger on mobile devices.
-
4esn0k over 8 yearswindow.matchMedia("(any-pointer: coarse)").matches === true ?
-
phreakhead over 8 yearsWell, it works on Chrome 47 for OS X, at least. Reporting no ontouchstart.
-
Sebastien Lorber almost 8 yearsgreat answer. Hopefully, the user always has a screen! I think it makes sense to build an interface where that ajust to current interaction mode of the user. On a touch laptop, it makes sense to adjust the app (ie the
:hover
elements and things like that) when user switch from mouse to touch. It seems unlikely that the user is currently using mouse + touch at the exact same time (I mean comoonn it's like having 2 mouses connected to the same computer hahaha) -
0xcaff almost 8 yearsIf you have a touchscreen with a mouse, only the input method used first will be detected.
-
0xcaff almost 8 years@suncat100 .@GameAlchemist your suggestions won't work on devices with both a touch screen and mouse.
-
suncat100 almost 8 years@GameAlchemist I said detect a touchstart event, not touch capability. If the visitor is on dual-input, if touch interaction occurs first, then it is incredibly likely that the visitor is going to continue this (even if a mouse is present). If mouse motion occurs before any touch event, it is incredibly likely visitor will continue using the mouse. The only way this could break, would be if the visitor is interchanging between touch screen and mouse to interact with the website, within the same browsing session. Thus, one should not only use this method progressively.
-
suncat100 almost 8 yearsAdding to the original topic question, you can add listeners for BOTH touch and mouse events (I mean listening for an actual event, not "touch" capability of the device). When they happen, you know they exist. It is not possible however as you note, to exclude that the visitor has mouse, just by detecting touch or even touch occurring. It is just very plausible by detecting actual usage of a specific device, that the visitor will continue using the same input method, on a dual-input device.
-
GameAlchemist almost 8 years@suncat100 : you must be replying to caffinatedmonkey, since i think we say pretty much the same : hook both touch and mouse events, see which one triggers first, then stick to that one.
-
suncat100 almost 8 years@gameAlchemist : Right, sorry 'bout that! :D
-
Engineer over 5 yearsThis is now so outdated that it is not relevant any longer.
-
MQuiggGeorgia over 4 yearsI personally like this answer but as of now (10/19), @media hover and pointer CSS queries are only available on ~85% of devices worldwide according to caniuse.com. Certainly not bad, 95% or above is preferable. Hopefully this will become standard on devices soon.
-
Blackbam over 4 years@MQuiggGeorgia Basically I agree with your criticism basically, it is not supported everywhere yet. Still caniuse.com for me says it is supported 91.2% (caniuse.com/#feat=css-media-interaction). Having a closer look it is supported everywhere except for IE 11 and special flattened browsers on mobile. To be fair this is true for any modern feature, as Microsoft stopped implementing IE features long ago. For IE 11 you might use a fallback from the other answers here.
-
ashleedawg over 4 years@SebastienLorber - hate to break it to you but users don't necessarily always have a screen. (Is it possible to use javascript to detect if a screen reader is running on a users machine?)
-
FrancescoMM over 4 yearsiPad, iPhone, Android triggere the musemouve on a touch event but you can avoid this behaviour by listening to the touch events and sending a event.preventDefault(). That shpuld stop the mousemove event to be triggered on touch, so if it gets triggered it is a real mouse event
-
raquelhortab over 3 yearsSeptember 2020: I'm trying the media match (hover: hover) in an android smartphone and it matches while in the w3 link says it shouldn't
-
Blackbam over 3 years@raquelhortab If the W3 says it shouldn't than it shouldn't. Which browser do you use?
-
raquelhortab over 3 years@Blackbam Chrome 85.0.4183.101 on Android 8.1.0 Aquaris V Build. I found a workaround to my problem anyway so I didn't look much into it
-
vsync about 3 yearsYou can have both mouse and touchscreen (some windows laptops have screen touch capabilities for years...)
-
Cluster almost 3 years
window.matchMedia("(any-pointer: fine)").matches
returns true on all my mobile browsers and desktop for some reason.window.matchMedia("(any-hover: hover)").matches
always returns true too, even on mobile devices without a mouse. Onlywindow.matchMedia("(any-pointer: coarse)").matches
returns true on mobile devices and false on desktop but it doesn't take into account connected mouse or s-pen. -
Cluster almost 3 years
window.matchMedia("(any-pointer: coarse)").matches
returns true on mobile devices and false on desktop but it doesn't take into account connected mouse or s-pen. -
Blackbam almost 3 years@Cluster This article describes the features in detail from a practical point of view: css-tricks.com/touch-devices-not-judged-size
-
Robin93K about 2 years
window.matchMedia("(any-hover: none)").matches
on any chromium browser on desktop always returnsfalse
, whilewindow.matchMedia("(any-pointer: fine)").matches
on any chromium browser on touch laptops returnsfalse
, so despite caniuse saying this works, it absolutely doesn't return the correct values in chromium browsers! -
Blackbam about 2 years@Robin93K What I would recommend you is the following article from CSS tricks: css-tricks.com/…
-
Robin93K about 2 years@Blackbam That article is nice, but it also fails to mention this solution still won't work in chromium browsers even if you do understand how to use it, as the media queries in chromium on windows simply do not work correctly. Simply execute
window.matchMedia("(any-hover: none)").matches
in the developer tools on any page in the chromium browser of your choice (chrome, vivaldi, iron, edge). Firefox is the only browser that fully supports "CSS4 media interaction". -
Blackbam about 2 years@Robin93K If not yet everything works as intended in chromium-based browsers they will hopefully implement it. It is still a candidate recommendation draft, not yet a standard (w3.org/TR/mediaqueries-4/#mf-interaction). Nevertheless it is the best solution I have encountered for this purpose so far. In my use cases it all worked, but I did not yet use the any- variants in practice yet.