可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'd like to provide separate behaviour for browsers supporting hover (e.g. desktop browsers) and ones which don't (e.g. touchscreen devices). Specifically I want to declare a hover state on browsers that support it, but not for browsers that don't, so as to avoid having mobile browsers emulate it with extra taps, as this breaks other interactions on the page - by not defining a hover state for those browsers this is avoided.
I've read up on the Interaction Media Queries feature and it looks like it should do the trick. I'd be able to do something like:
@media (hover: none) {
/* behaviour for touch browsers */
}
According to CanIUse it is available on all the browsers I need to support except IE11 and Firefox.
So I wondered if I could do it the other way around - since the main touch devices all support it, then negate it:
@media not (hover: none) {
/* behaviour for desktop browsers */
}
However, this doesn't seem to work at all.
Pseudocode example of what I'm trying to do:
.myelement {
/* some styling */
/* note: no hover state here */
}
@media(this device supports hover) {
.myelement:hover {
/* more styling */
}
}
So, is there a way to make this work in the way intended, or am I down the wrong track?
回答1:
not
should prefix media type (screen, print, all, etc) and not media feature (hover, point, etc).
Wrong:
@media not (hover: none)
Correct:
@media not all and (hover: none)
Yes, its unintuitive and weird. Source (see comments).
So you do not need javascript to alternate result of media query.
回答2:
From the specs:
none
Indicates that the primary pointing system can’t hover, or there is no pointing system. Examples include touchscreens and screens that use a drawing stylus.
Pointing systems that can hover, but for which doing so is inconvenient and not part of the normal way they are used, also match this value.
For example, a touchscreen where a long press is treated as hovering would match hover: none.
If your browser (mobile/touch) support long-press to simulate hover, the usage of hover: none
will not work. What you can do is just use a default value and override it (with default css precedence):
body {
background: red;
}
@media (hover: hover) {
body {
background: blue;
}
}
Desktop will have blue
background and mobile/touch will have red
background
Check the following example:
https://jsfiddle.net/mcy60pvt/1/
To check the long-press option of the mobile you can use this example:
https://jsfiddle.net/mcy60pvt/3/
In the above example the green block has :hover
definition for both desktop and mobile, however for desktop the background will change to yellow
and for mobile (with long-press) it will change to white
.
Here is the css for the last example:
body {
background: red;
}
div.do-hover {
border: 1px solid black;
width: 250px;
height: 250px;
background: green;
}
div.do-hover:hover {
background: white;
}
@media (hover: hover) {
body {
background: blue;
}
div.do-hover:hover {
background: yellow;
}
}
In this example - browsers that don't support :hover
will view the hover me box with green background, and while "hover" (touch/long-press) - the background will change to white.
update
As for the added pseudo code:
.myelement {
/* some styling #1 */
/* note: no hover state here */
}
@media(hover: hover) {
.myelement {
/* some styling that override #1 styling in case this browser suppot the hover*/
}
.myelement:hover {
/* what to do when hover */
}
}
回答3:
Thanks to Dekel's comments I solved this by running the logic in JS and applying a class instead:
e.g.
const canHover = !(matchMedia('(hover: none)').matches);
if(canHover) {
document.body.classList.add('can-hover');
}
Then in the stylesheet:
.myElement {
background: blue;
}
.can-hover .myElement:hover {
background: red;
}
I've tested this on desktop Chrome, Safari and Firefox, and iOS Safari and it works as expected.
回答4:
According to Artin´s answer we can address only devices that support hover with pure css, 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;
}
}
}
The following would change background-color of .container
from red to blue on hover for devices that support hover, no change for touch devices:
.container {
background-color: red;
@include hover-supported() {
background-color: blue;
}
}
回答5:
To support Firefox (see https://bugzilla.mozilla.org/show_bug.cgi?id=1035774 ), you need to potentially write some rules twice. Note, although not specified in the question I've added pointer: coarse
on the assumption that the purpose of these rules is to target mobile screens:
/* BEGIN devices that DON'T pinch/zoom */
/* If https://bugzilla.mozilla.org/show_bug.cgi?id=1035774
is fixed then we can wrap this section in a ...
@media not all and (pointer: coarse) and (hover: none) {
.. and do away with the 'NEGATE' items below */
.myelement {
/* some styling that you want to be desktop only (style as an anchor on desktop) */
font-size: 14px;
text-decoration: underline;
border: none;
}
/* END devices that DON'T pinch/zoom */
/* BEGIN devices that DO pinch/zoom */
@media (pointer: coarse) and (hover: none) {
.myelement {
/* style as a large button on mobile */
font-size: inherit; /* maintain e.g. larger mobile font size */
text-decoration: none; /* NEGATE */
border: 1px solid grey;
}
.myelement:hover {
/* hover-only styling */
}
}
/* END devices that DO pinch/zoom */
The combination of (pointer: coarse) and (hover: none)
should become more useful to target mobile devices as mobile screens become larger and we lose the correlation between pixel dimensions and mobile vs. desktop (already the case when you wish to distinguish between tablet and desktop)