What is the safest way, using media queries, to make something happen when not on a touchscreen device? If there is no way, do you suggest using a JavaScript solution such as !window.Touch
or Modernizr?

- 347,512
- 102
- 1,199
- 985

- 5,837
- 19
- 56
- 78
-
9Fast forward to 2018, CSS is now natively able to do this https://stackoverflow.com/a/50285058/1717735 – Buzut Nov 26 '18 at 14:12
14 Answers
There is actually a property for this in the CSS4 media query draft.
The ‘pointer’ media feature is used to query about the presence and accuracy of a pointing device such as a mouse. If a device has multiple input mechanisms, it is recommended that the UA reports the characteristics of the least capable pointing device of the primary input mechanisms. This media query takes the following values:
‘none’
- The input mechanism of the device does not include a pointing device.‘coarse’
- The input mechanism of the device includes a pointing device of limited accuracy.‘fine’
- The input mechanism of the device includes an accurate pointing device.
This would be used as such:
/* Make radio buttons and check boxes larger if we have an inaccurate pointing device */
@media (pointer:coarse) {
input[type="checkbox"], input[type="radio"] {
min-width:30px;
min-height:40px;
background:transparent;
}
}
I also found a ticket in the Chromium project related to this.
Browser compatibility can be tested at Quirksmode. These are my results (22 jan 2013):
- Chrome/Win: Works
- Chrome/iOS: Doesn't work
- Safari/iOS6: Doesn't work

- 249,484
- 69
- 436
- 539

- 23,374
- 22
- 80
- 109
-
5How can this be the answer with the most points when the provided solution doesn't work even according to the poster? – erdomester Aug 18 '16 at 18:38
-
7Browser support for this is not as bad any more: Edge, Chrome, Safari, iOS and Android. See: http://caniuse.com/#feat=css-media-interaction – Josa Jun 09 '17 at 12:58
-
6Good answer. Additionally: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover – aross Dec 11 '17 at 14:54
-
-
3tested today on Chrome, Firefox and Safari apps (iOS 14.4) and all worked – Doug Dec 21 '20 at 04:19
Nowadays, CSS Media queries can be used to define style for devices with specific interactive features and it's widely supported as well.
hover for example can be used to test whether the user's primary input mechanism can hover over elements (which would not be true in touch-enabled devices without emulating)
@media (hover: none) {
a {
background: yellow;
}
}
Other interactive tests are: pointer, any-pointer, hover, and any-hover
Previous answer
I would suggest using modernizr and using its media query features.
if (Modernizr.touch){
// bind to touchstart, touchmove, etc. and watch `event.streamId`
} else {
// bind to normal click, mousemove, etc.
}
However, using CSS, there are pseudo class like, for example in Firefox. You can use :-moz-system-metric(touch-enabled). But these features are not available for every browser.
For Apple devices, you can simply use:
if (TouchEvent) {
//...
}
Especially for iPad:
if (Touch) {
// ...
}
But, these do not work on Android.
Modernizr gives feature detection abilities, and detecting features is a good way to code, rather than coding on basis of browsers.
Styling Touch Elements
Modernizer adds classes to the HTML tag for this exact purpose. In this case, touch
and no-touch
so you can style your touch related aspects by prefixing your selectors with .touch. e.g. .touch .your-container
. Credits: Ben Swinburne
-
Am I right in thinking that no psedo classes work with all modern devices/browsers? – JJJollyjim Jul 09 '12 at 00:32
-
@JJ56, Yes not every browser support touch pseudo classes. But that is where `modernizr` will be perfect :) – Starx Jul 09 '12 at 00:34
-
21The `Modernizr.touch` test only indicates if the browser supports touch events, which does not necessarily reflect a touchscreen device. For example, Palm Pre / WebOS (touch) phones do not support touch events and thus fail this test. – numediaweb Feb 14 '13 at 18:58
-
5Just to add to this, if you're including modernizer anyway; Modernizer adds classes to the body tag for this exact purpose. In this case, `touch` and `no-touch` so you can style your touch related aspects by prefixing your selectors with `.touch`. E.g. `.touch .your-container` – Ben Swinburne Sep 01 '13 at 18:05
-
2For me it seems that modernizr add touch class to the html tag, not the body tag. – Robin Cox Mar 19 '14 at 18:48
-
Just confirming that `modernizr` adds these classes to the `html` element, not the `body` element. – Dave Sag Nov 29 '15 at 08:27
-
-
Since this is the accepted answer and this question comes up on Google, it may be worth adding a note about doing this with media queries, since [they are now supported in 95% of browsers](https://caniuse.com/css-media-interaction). – Artemis Mar 21 '21 at 22:04
-
2Does it make sense to have a :hover inside a hover: none query? It would do nothing in that case, right? Maybe you want hover: hover? – Greg Bell Aug 21 '21 at 02:56
-
@GregBell - Yeah, I noticed that too. I did some testing in jsFiddle, and confirmed that having a `:hover` inside of a `hover: none` mean that the rule will do absolutely nothing. I've edited the `:hover` out of the answer to avoid confusion in the future. – Skeets Aug 22 '21 at 23:35
-
Chrome 105 on an Android tablet matches `hover: hover`, so what's the use of this after all? – ygoe Jul 16 '22 at 10:11
-
The @media (hover: none) is a little problematic when you inspect elements in the browser as a dev. – Stefanos Karakasis Aug 03 '22 at 15:35
The CSS solutions don't appear to be widely available as of mid-2013. Instead...
Nicholas Zakas explains that Modernizr applies a
no-touch
CSS class when the browser doesn’t support touch.Or detect in JavaScript with a simple piece of code, allowing you to implement your own Modernizr-like solution:
<script> document.documentElement.className += (("ontouchstart" in document.documentElement) ? ' touch' : ' no-touch'); </script>
Then you can write your CSS as:
.no-touch .myClass { ... } .touch .myClass { ... }

- 2,919
- 1
- 15
- 11

- 55,742
- 17
- 139
- 133
-
-
@bjb568 Thanks for the suggested edit, which is a more modern alternative, but as it only works in IE 10 and above, I think it's best to keep this version for the time being. – Simon East Jan 12 '16 at 10:29
-
Sounds like that class would be on the html tag; which then would mean it's disabled by default when using CSP. – Leo Nov 21 '16 at 19:54
-
You could also put scss like a media query in the form: `html.touch &` which will write the selector you have under the parent `html.touch`. – Stefanos Karakasis Jul 21 '22 at 15:51
-
@Leo - I know your comment was 6 years ago, but in case anyone else reads this, yes [CSP](https://content-security-policy.com/) disables inline ` – Simon East Jul 22 '22 at 00:30
There is actually a media query for that:
@media (hover: none) { … }
Apart from Firefox, it's fairly well supported. Safari and Chrome being the most common browsers on mobile devices, it might suffice untill greater adoption.

- 4,875
- 4
- 47
- 54
-
4
-
Chrome 105 on an Android tablet matches `hover: hover`, so what's the use of this after all? – ygoe Jul 16 '22 at 10:10
Media types do not allow you to detect touch capabilities as part of the standard:
http://www.w3.org/TR/css3-mediaqueries/
So, there is no way to do it consistently via CSS or media queries, you will have to resort to JavaScript.
No need to use Modernizr, you can just use plain JavaScript:
<script type="text/javascript">
var is_touch_device = 'ontouchstart' in document.documentElement;
if(is_touch_device) alert("touch is enabled!");
</script>

- 37,233
- 13
- 109
- 109
-
AFAIK, `window.touch` && `window.TouchEvent` only work for apple devices. – Starx Jul 09 '12 at 00:39
-
22@vsync Modernizr is a great way to put all tests useful for developers in a mantained library, for them to not have to Google each time they need to test a specific feature, like touch events and having to find this page. FYI there's a custom Modernizr build that you can customize with the tests you want. It's a great way of testing features always the same way between projects, either you use the JS syntax (ie: Modernizr.touch) or the CSS classes (.touch .my-element {}). I think it's an insult to human intelligence not recognizing that we can do things without reinventing the wheel each time. – Alejandro García Iglesias Apr 10 '13 at 21:39
-
11Well, I've worked on hundreds of projects and I'de never needed such a thing, in most case all you need is a very small set of tests, never more than 5 I would say (in most cases), so just find those 5 and put them in your code directly, no need to actually go to Modernizer website, make a custom build, include it in your page, or whatever. Developers shouldn't be THAT lazy. A web developer should always have the mindset of "how can I make the code lighter?"..IMHO and years of experience. developers nowadays seems to love including tons of scripts :/ – vsync Apr 11 '13 at 08:24
-
2Some say lazy. I say why reinvent the wheel? What about when a new device/version/platform appears and your special case needs changing? Do you a) Attempt to work out special case detection code yourself, and backport to your custom code, reacquainting yourself with it in the process, or b) download a more recent version of Modernizr and drop it in? I know which I'd do. +1 to GarciaWebDev. – Sc0ttyD Apr 12 '13 at 09:26
-
@Sc0ttyD My personal preference is to learn to code things initially without depending on libraries such as Modernizr. Some day you might need to be that person coding for the new device and you don't want to be stuck because you can't do it without a library. Not to mention, there's probably a lot of people who just don't have the time at their job to learn a new library. – Alex W Apr 12 '13 at 13:44
-
1@Sc0ttyD sometimes a wheel is not what you need. sometimes you need skis or wings. so glibly refusing to do what is required because "it's reinventing the wheel" results in weird buggies rolling down snow slopes, or crumpled bycylces at the bottom of a cliff, because. well doh, wheels don't produce lift. – unsynchronized Aug 26 '16 at 09:54
In 2017, CSS media query from second answer still doesn't work on Firefox. I found a soluton for that: -moz-touch-enabled
So, here is cross-browser media query:
@media (-moz-touch-enabled: 1), (pointer:coarse) {
.something {
its: working;
}
}
-
this should be higher up and the approved answer. firefox is working on implementing the spec, so `-moz-touch-enabled` will not be required soon – Dogoku Jan 04 '18 at 14:37
-
can be updated after 11. Dec. '18 https://wiki.mozilla.org/Release_Management/Calendar https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer#Browser_compatibility – retrovertigo Nov 26 '18 at 02:57
-
@user3445853 OP asks how to detect (via media query) the device with a touchscreen. If rule "(pointer:none)" will be true, then the device doesn't have a touchscreen for sure. But someone should confirm my opinion (or no) :) – Sokołow Jan 14 '19 at 21:54
-
`-moz-touch-enabled` is supposedly not supported in Firefox 58+. As in, this solution doesn't cover Firefox 58-63 (since Firefox 64+ supports the pointer query). Source: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/-moz-touch-enabled – fgblomqvist Apr 23 '19 at 21:45
This will work. If it doesn't let me know
@media (hover: none) and (pointer: coarse) {
/* Touch screen device style goes here */
}
hover:none is true when hover is not supported
pointer:coarse is true when the input is of limited accuracy.
If all you want to do is to control hover, then you dont need to use pointer.
edit: hover on-demand is not supported anymore

- 4,022
- 20
- 31
- 41

- 251
- 3
- 7
-
Chrome 105 on an Android tablet matches `hover: hover`, so what's the use of this after all? – ygoe Jul 16 '22 at 10:11
-
This solution will work until CSS4 is globally supported by all browsers. When that day comes just use CSS4. but until then, this works for current browsers.
browser-util.js
export const isMobile = {
android: () => navigator.userAgent.match(/Android/i),
blackberry: () => navigator.userAgent.match(/BlackBerry/i),
ios: () => navigator.userAgent.match(/iPhone|iPad|iPod/i),
opera: () => navigator.userAgent.match(/Opera Mini/i),
windows: () => navigator.userAgent.match(/IEMobile/i),
any: () => (isMobile.android() || isMobile.blackberry() ||
isMobile.ios() || isMobile.opera() || isMobile.windows())
};
onload:
old way:
isMobile.any() ? document.getElementsByTagName("body")[0].className += 'is-touch' : null;
newer way:
isMobile.any() ? document.body.classList.add('is-touch') : null;
The above code will add the "is-touch" class to the body tag if the device has a touch screen. Now any location in your web application where you would have css for :hover you can call body:not(.is-touch) the_rest_of_my_css:hover
for example:
button:hover
becomes:
body:not(.is-touch) button:hover
This solution avoids using modernizer as the modernizer lib is a very big library. If all you're trying to do is detect touch screens, This will be best when the size of the final compiled source is a requirement.

- 3,488
- 1
- 20
- 31
-
This JS posted looks effective and compact; likely still viable for use today? What about newer mobile browsers (Firefox, MS Edge, Brave)? Does this JS line go inside the script (... isMobile.any() ? document.body.classList.add('is-touch') : null; ...) before the export const code as a variable? Thanks. – granite May 08 '23 at 00:01
I believe the better way to query a touch-screen with CSS (in 2022) is with (pointer:coarse)
, and definitely NOT with (hover:none)
, or a combination, as some suggest: (hover: none) and (pointer: coarse)
, because (hover:none)
is unreliable - some devices emulate hover on long press (touch) so (hover:none)
will not be true.
I had such a problem, on a real touch device (a phone, not an emulation), without any other input (mouse or something), that "had" a hover capability, and my media query failed.
Probably the best way is to use Modernizr, or even both (Modernizr and CSS, though I didn't look what tests Modernizr does, might be the same as CSS :) like:
.modernizr-touch-class .my-class { // properties }
@media (pointer:coarse) { .my-class { // the same properties } }
But all in all, this is not considered reliable. When you add modernizr "Touch Events" test, they display a short info (with a few links) on this problem. This is probably a good thing to read to find the best solution to a particular problem, and then test it...
Anyway, I just wanted to say that (hover:none)
is unreliable (in 2022). :D

- 51
- 5
This works fine on Mozila-based and Webkit-based browsers:
/* show only on mouse devices */
@media (hover: hover), (-moz-touch-enabled: 0), (pointer:fine) {
...
}
/* show only on touch devices */
@media (hover: none), (hover: on-demand), (-moz-touch-enabled: 1), (pointer:coarse) {
...
}

- 131
- 1
- 3
adding a class touchscreen to the body using JS or jQuery
//allowing mobile only css
var isMobile = ('ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/));
if(isMobile){
jQuery("body").attr("class",'touchscreen');
}

- 6,851
- 11
- 54
- 77
I was able to resolve this issue using this
@media (hover:none), (hover:on-demand) {
Your class or ID{
attributes
}
}
-
FYI, `on-demand` has been removed from the spec and from browsers: https://github.com/w3c/csswg-drafts/commit/2078b46218f7462735bb0b5107c9a3e84fb4c4b1 – retrovertigo Nov 26 '18 at 02:54
As of 5/5/2022:
var isTouch = "maxTouchPoints" in window.navigator && window.navigator.maxTouchPoints > 1;
var isTouchTwo = "ontouchstart" in window;
var isTouchThree = window.matchMedia && window.matchMedia("(pointer:coarse)").matches;
var isDesktopIE = !!window.MSInputMethodContext && !!document.documentMode && !isTouchTwo;
var uaDataIsMobile = window.navigator.userAgentData && window.navigator.userAgentData.mobile;
var isDevice = typeof uaDataIsMobile === 'boolean' ? window.navigator.userAgentData.mobile || (isTouchTwo && isTouchThree) : ((/android|avantgo|blackberry|googlebot-mobile|iemobile|opera mini|kitkat|palmos|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|iphone|ipod|phone|ipad|pie|tablet|silk|up\.browser|up\.link|playbook|webos|wos/i.test(window.navigator.userAgent.toLowerCase()) && !isDesktopIE) || window.navigator.platform === 'MacIntel' && isTouch);
console.log('1. '+isTouch+' 2. '+isTouchTwo+' 3. '+isTouchThree+' 4. '+/android|avantgo|blackberry|googlebot-mobile|iemobile|opera mini|kitkat|palmos|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|iphone|ipod|phone|ipad|pie|tablet|silk|up\.browser|up\.link|playbook|webos|wos/i.test(window.navigator.userAgent.toLowerCase())+' 5. isDevice: '+isDevice)
Redresses issues, a) where IE11 desktop navigator.userAgent
string claims to be "tablet", and b) where iPadOS User Agent in Safari is same as on MacOS Catalina. (Untested by me, but it follows this line of argumentation
quote
https://developer.apple.com/forums/thread/119186
unquote
and should work. I'm depending on it. (See edit.)
Note that my answer is compatible with Client Hints. (Future answers should also be, I think.) For browsers that support navigator.userAgentData
, you won't need navigator.userAgent
.
Sorry about the long strings. In ES6 it looks prettier.
To do a media query, just attach a style sheet based on true or false output.
edit: How to detect iPad and iPad OS version in iOS 13 and Up?
if (navigator.maxTouchPoints > 1) {
// browser supports multi-touch
}
I thought it was > 0
. (Corrected in code.)

- 11
- 2
I tried various options on various browsers and found that you need to use the hover and pointer both -
@media (hover:none), (pointer:coarse){
.show_on_touch {display:inline}
}
The hover:none tells you that there is no hover supported, but some browsers on some platforms lie.
And pointer:coarse tells you that fine pointer control is absent, so it may be finger based.

- 4,022
- 20
- 31
- 41