Detect if a client device supports :hover and :focus states

css, hover, html, javascript, jquery

Solution

The W3C seems to have recognized this problem and has introduced the hover feature:

The hover media feature is used to query the user’s ability to `hover` over elements on the page with the primary pointing device. If a device has multiple pointing devices, the hover media feature must reflect the characteristics of the “primary” pointing device, as determined by the user agent. (To query the capabilities of any available pointing devices, see the any-hover media feature.)

There is even a media query to check if there is any possibility to hover:

The `any-pointer` and `any-hover` media features are identical to the pointer and hover media features, but they correspond to the union of capabilities of all the pointing devices available to the user. In the case of any-pointer, more than one of the values can match, if different pointing devices have different characteristics.

Code samples:

/* 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) { ... }

Official draft: https://drafts.csswg.org/mediaqueries/#hover

This feature is still at risk, but I really hope it will be fully supported soon as it is already widely supported: https://caniuse.com/#feat=css-media-interaction

Further read: https://css-tricks.com/touch-devices-not-judged-size/

For Chrome test your device here: https://googlechrome.github.io/samples/media-hover-pointer/

Test with JavaScript: https://jsfiddle.net/Blackbam/zkd2cs0t/16/

The best solution for now is most probably to use those media queries with a fallback solution using touch detection via `document.createEvent("TouchEvent");` and mouse detection via `mousemove.hasMouse`.

Problem

Sounds like a simple problem, but turns out to be quite challenging to solve. For some website I have contents that are only to be shown if a user hovers/focuses a link. The link however has a target itself. If one of those links is clicked by a touch screen user the browser instantly goes to the `href` location. This means the hover contents are never visible! This is why users which do not have a mouse (or another device to hover like a magic remote control) should see alternative content. But how can I detect this? ``` $(document).on('click','#my-menu-inner > ul > li > a',function(e) { if(clientHasInputDeviceSupportingHover()) { return true; } else { e.preventDefault(); $('#for-no-hover-visitors').html(''); $(this).clone().appendTo('#for-no-hover-visitors'); $(this).next().clone().appendTo('#for-no-hover-visitors'); } }); function clientHasInputDeviceSupportingHover() { // HOW CAN I DETECT THIS??? if($('#checkhover:checked').length > 0) { return true; } return false; } ``` ``` .clearfix::after { content: ""; clear: both; display: table; } #my-menu-inner > ul { margin:10px; width:100%; background-color:yellow; list-style-type:none; position:relative; } #my-menu-inner > ul > li { float:left; margin:20px; } #my-menu-inner > ul > li > a { padding:20px; border:1px solid black; display:block; } #my-menu-inner > ul > li > div.sub { position:absolute; top:calc(100% - 20px); background-color:red; padding:40px; display:none; left:0; width:100vw; } #my-menu-inner > ul > li a:hover + div.sub, #my-menu-inner > ul > li a:focus + div.sub, #my-menu-inner > ul > li > div.sub:hover, #my-menu-inner > ul > li > div.sub:focus { display:block; } ``` ``` <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> Simulate for Client supporting hover: <input type="checkbox" id="checkhover" /> <div id="my-menu"> <div id="my-menu-inner"> <ul class="clearfix"> <li> <a href="http://www.example.com/foo/">foo</a> <div class="sub"> <ul> <li><a href="http://www.example.com/mobile/">mobile</a></li> <li><a href="http://www.example.com/users/">users</a></li> </ul> </div> </li> <li> <a href="http://www.example.com/bar/">bar</a> <div class="sub"> <ul> <li><a href="http://www.example.com/never/">never</a></li> <li><a href="http://www.example.com/see-me/">see me</a></li> </ul> </div> </li> </ul> </div> </div> <div id="for-no-hover-visitors"></div> ``` The problem is `clientHasInputDeviceSupportingHover()`. What is the most reliable way to find this out? What we know so far It is possible to detect a touch device: What's the best way to detect a 'touch screen' device using JavaScript? Mouse detection at least might work"onclick": How to detect if a device has mouse support? In general there are a lot of different possible input devices: https://en.wikipedia.org/wiki/Input_device#Pointing_device A generic / more reliable solution would be very welcome.

Original source

Related problems