How to remove/ignore :hover css style on touch devices
2020 Solution - CSS only - No Javascript
Use media hover with media pointer will help you resolve this issue. Tested on chrome Web and android mobile. I known this old question but I didn't find any solution like this.
@media (hover: hover) and (pointer: fine) {
a:hover { color: red; }
}
<a href="#" >Some Link</a>
According to Jason´s answer we can address only devices that doesn't support hover with pure css media queries. We can also address only devices that support hover, like moogal´s answer in a similar question, with
@media not all and (hover: none)
. It looks weird but it works.
I made a Sass mixin out of this for easier use:
@mixin hover-supported {
@media not all and (hover: none) {
&:hover {
@content;
}
}
}
Update 2019-05-15: I recommend this article from Medium that goes through all different devices that we can target with CSS. Basically it's a mix of these media rules, combine them for specific targets:
@media (hover: hover) {
/* Device that can hover (desktops) */
}
@media (hover: none) {
/* Device that can not hover with ease */
}
@media (pointer: coarse) {
/* Device with limited pointing accuracy (touch) */
}
@media (pointer: fine) {
/* Device with accurate pointing (desktop, stylus-based) */
}
@media (pointer: none) {
/* Device with no pointing */
}
Example for specific targets:
@media (hover: none) and (pointer: coarse) {
/* Smartphones and touchscreens */
}
@media (hover: hover) and (pointer: fine) {
/* Desktops with mouse */
}
I love mixins, this is how I use my hover mixin to only target devices that supports it:
@mixin on-hover {
@media (hover: hover) and (pointer: fine) {
&:hover {
@content;
}
}
}
button {
@include on-hover {
color: blue;
}
}
tl;dr use this: https://jsfiddle.net/57tmy8j3/
If you're interested why or what other options there are, read on.
Quick'n'dirty - remove :hover styles using JS
You can remove all the CSS rules containing :hover
using Javascript. This has the advantage of not having to touch CSS and being compatible even with older browsers.
function hasTouch() {
return 'ontouchstart' in document.documentElement
|| navigator.maxTouchPoints > 0
|| navigator.msMaxTouchPoints > 0;
}
if (hasTouch()) { // remove all the :hover stylesheets
try { // prevent exception on browsers not supporting DOM styleSheets properly
for (var si in document.styleSheets) {
var styleSheet = document.styleSheets[si];
if (!styleSheet.rules) continue;
for (var ri = styleSheet.rules.length - 1; ri >= 0; ri--) {
if (!styleSheet.rules[ri].selectorText) continue;
if (styleSheet.rules[ri].selectorText.match(':hover')) {
styleSheet.deleteRule(ri);
}
}
}
} catch (ex) {}
}
Limitations: stylesheets must be hosted on the same domain (that means no CDNs). Disables hovers on mixed mouse & touch devices like Surface or iPad Pro, which hurts the UX.
CSS-only - use media queries
Place all your :hover rules in a @media
block:
@media (hover: hover) {
a:hover { color: blue; }
}
or alternatively, override all your hover rules (compatible with older browsers):
a:hover { color: blue; }
@media (hover: none) {
a:hover { color: inherit; }
}
Limitations: works only on iOS 9.0+, Chrome for Android or Android 5.0+ when using WebView. hover: hover
breaks hover effects on older browsers, hover: none
needs overriding all the previously defined CSS rules. Both are incompatible with mixed mouse & touch devices.
The most robust - detect touch via JS and prepend CSS :hover rules
This method needs prepending all the hover rules with body.hasHover
. (or a class name of your choice)
body.hasHover a:hover { color: blue; }
The hasHover
class may be added using hasTouch()
from the first example:
if (!hasTouch()) document.body.className += ' hasHover'
However, this whould have the same drawbacks with mixed touch devices as previous examples, which brings us to the ultimate solution. Enable hover effects whenever a mouse cursor is moved, disable hover effects whenever a touch is detected.
function watchForHover() {
// lastTouchTime is used for ignoring emulated mousemove events
let lastTouchTime = 0
function enableHover() {
if (new Date() - lastTouchTime < 500) return
document.body.classList.add('hasHover')
}
function disableHover() {
document.body.classList.remove('hasHover')
}
function updateLastTouchTime() {
lastTouchTime = new Date()
}
document.addEventListener('touchstart', updateLastTouchTime, true)
document.addEventListener('touchstart', disableHover, true)
document.addEventListener('mousemove', enableHover, true)
enableHover()
}
watchForHover()
This should work basically in any browser and enables/disables hover styles as needed.
Here's the full example - modern: https://jsfiddle.net/57tmy8j3/
Legacy (for use with old browsers): https://jsfiddle.net/dkz17jc5/19/
Pointer adaptation to the rescue!
Since this hasn't been touched in awhile, you can use:
a:link, a:visited {
color: red;
}
a:hover {
color:blue;
}
@media (hover: none) {
a:link, a:visited {
color: red;
}
}
See this demo in both your desktop browser and your phone browser. Supported by modern touch devices.
Note: Keep in mind that since a Surface PC's primary input (capability) is a mouse, it will end up being a blue link, even if it's a detached (tablet) screen. Browsers will (should) always default to the most precise input's capability.