Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting that the browser has no mouse and is touch-only

People also ask

Is it possible to detect when the mouse is over the browser tab?

It's not possible. All you can do is attach a mouseleave event to the document so you know when the mouse leaves the document, but not exactly where outside it that it went.

How do I know if touch events are available?

One way to check for a touch screen device is to check for the window. ontouchstart method and the navigator. maxTouchPoints property. ontouchstart lets us assign a touch event listener to it that runs when we start touching the screen.


The main trouble is that you have the following different classes of devices/use cases:

  1. Mouse and keyboad (desktop)
  2. Touch only (phone/tablet)
  3. Mouse, keyboard, and touch (touch laptops)
  4. Touch and keyboard (bluetooth keyboard on tablet)
  5. Mouse only (Disabled user/browsing preference)
  6. Keyboard only (Disabled user/browsing preference)
  7. 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 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.


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 sth
}

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


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...


@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:

  1. touchstart
  2. touchmove
  3. touchend
  4. mouseover
  5. mousemove
  6. mousedown
  7. mouseup
  8. 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


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 event 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".