如何在触屏设备上删除/忽略:hover CSS样式

253

如果用户通过触摸设备访问我们的网站,我希望忽略所有的 :hover CSS 声明。因为 :hover 在这种情况下是没有意义的,而且如果一个平板电脑在点击/轻敲时触发它,则可能会一直持续到元素失去焦点,甚至可能会干扰用户体验。说实话,我不知道为什么触摸设备需要首先触发 :hover,但这是现实,所以这个问题也是真实存在的。

a:hover {
  color:blue;
  border-color:green;
  /* etc. > ignore all at once for touch devices */
}

那么,我应该如何一次性删除/忽略所有CSS :hover 声明(不需要知道每个声明)以适应触摸设备?


这与如何防止触摸设备上按钮的粘性悬停效果类似,但更为普遍。 - Simon Ferndriger
1
我用一个PHP解决方案来解决这个问题,其中我会检测查看设备是否为移动设备,并根据此使用正确的样式表进行各种更改。但这并没有回答你的问题。 你可以尝试使用@media查询,但当手机和平板电脑具有全高清分辨率时,这也不能保证有效。 - TomFirth
1
可能是在移动浏览器上禁用悬停效果的重复内容。 - rnevius
18个回答

233

简短版:使用https://jsfiddle.net/57tmy8j3/

如果您想了解为什么或其他选项,请继续阅读。

快速且有效 - 使用JS删除:hover样式

您可以使用Javascript删除所有包含:hover的CSS规则。这样做的优点是无需更改CSS,即使在旧浏览器中也兼容。

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) {}
}

限制条件:样式表必须托管在同一域名下(这意味着不能使用CDN)。禁用混合鼠标和触摸设备上的悬停效果,例如Surface或iPad Pro,这会损害用户体验。

CSS-only - 使用媒体查询

将所有的:hover规则放在一个@media块中:

@media (hover: hover) {
  a:hover { color: blue; }
}

或者,替代方式是覆盖所有的悬停规则(兼容旧版浏览器):

a:hover { color: blue; }

@media (hover: none) {
  a:hover { color: inherit; }
}

限制:仅在使用iOS 9.0+、Chrome for Android或Android 5.0+的WebView时工作。 hover: hover 在旧浏览器上会导致悬停效果中断,hover: none 需要覆盖先前定义的所有CSS规则。两者都与混合鼠标和触摸设备不兼容。

最稳健的方法 - 通过JS检测触摸并添加CSS :hover规则

使用此方法需要将所有悬停规则与body.hasHover(或您选择的类名)一起添加在前面。

body.hasHover a:hover { color: blue; }

可以使用第一个示例中的 hasTouch() 方法添加 hasHover 类:

if (!hasTouch()) document.body.className += ' hasHover'

然而,这种方法在使用混合触摸设备时会出现与之前示例相同的缺点,这就引出了最终解决方案:只有在鼠标光标移动时启用悬停效果,在检测到触摸时禁用悬停效果。

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()

这基本上适用于任何浏览器,并根据需要启用/禁用悬停样式。

这是完整的示例 - 现代浏览器:https://jsfiddle.net/57tmy8j3/
传统浏览器(用于旧浏览器):https://jsfiddle.net/dkz17jc5/19/


1
我使用 try/catch 以防万一出现一些奇怪的错误,可能会导致脚本崩溃,比如由于同源策略或 IE 上不稳定的支持而导致 deleteRule 无法工作(已编辑注释以反映这一点)。但是,在大多数情况下,只需要进行简单的检查就足够了。 - blade
1
值得一提的是,包括Firefox在内的桌面浏览器不会出现“notouch”。它们会报告理解触摸事件。 - Harry B
3
这个解决方案无法应用于混合设备,其中悬停效果只对鼠标有效,而不适用于触摸事件。 - nbar
这个解决方案是不正确的。如果你有一个像这样的选择器 li, a:hover { border: 1px solid red; }selectorText 会是 li, a:hover。使用这段代码会使所有的 li 失去它们的边框,只有 a:hover 应该失去它们的边框。 - Haneev
2
@Haneev,对于大多数情况来说,这是正确的,但不适用于您的特定情况。让您的规则起作用将会给代码增加很多复杂性,这会使答案更难理解。我建议要么拆分规则,要么使用第二个解决方案,它更加健壮。然而,如果您可以在不使第一个解决方案过于复杂的情况下改进它,欢迎您提出修改建议。 - blade
显示剩余7条评论

154

2020解决方案 - 纯CSS - 无Javascript

使用媒体悬停媒体指针可以帮助您解决此问题。在Chrome Web和Android移动设备上进行了测试。我知道这是一个旧问题,但我没有找到像这样的解决方案。

@media (hover: hover) and (pointer: fine) {
  a:hover { color: red; }
}
<a href="#" >Some Link</a>


2
IE 不支持 FYI - ftw
2
不支持在带触摸屏的笔记本电脑上的Chrome 92浏览器中运行。 - Maciej Krawczyk
2
我认为这是可能的,因为指针:良好的媒体查询评估不是实际使用的指针,而是主要指针 - 即鼠标。但在iOS上效果很好。 - Maciej Krawczyk
2
@MaciejKrawczyk 因为你的触摸屏会触发指针的“粗糙”属性。指针有两个选项。细和粗。细=主要输入机制包括准确的指向设备。 粗=主要输入机制包括精度有限的指向设备。也就是说,鼠标=细,但你的手指会是粗。 - Andrew West

56

hover CSS 媒体特性来拯救了!只需使用 CSS,您就可以在设备没有 hover 功能时覆盖样式。

下面的演示受到现代触控设备的支持。

/* hover query styles */

a {
  color: red;
  font-size: 3em;
}

a:hover {
  color: blue;
}

@media (hover: none) {
  a:link,
  a:visited {
    color: blue;
    text-decoration: none;
    border: 0.1em solid currentColor;
    padding: 0 0.1em;
  }
}

/* used to show if demo browser has hover capabilities */
.detection:before {
  content: 'UNKNOWN';
  color: red;
}

@media(hover) {
  .detection:before {
    content: 'YES';
    color: green;
  }
}

@media (hover: none) {
  .detection:before {
    content: 'NO';
  }
}
<p>Hoverable pointer detected: <span class="detection"></span></p>

<h3>Experiences between device hover capabilities</h3>

<p>If the device has a hover capability, the link below:</p>

<ul>
  <li>should be red</li>
  <li>should be blue when hovered</li>
</ul>

<p>If the device does not have a hover capability, the link below:</p>

<ul>
  <li>should always be blue</li>
  <li>should be surrounded by a blue border</li>
</ul>

<p><a href="https://www.google.com">Link</a></p>

注意:请记住,由于Surface PC的主要输入(功能)是鼠标,因此即使它是一个分离的(平板电脑)屏幕,它也将最终成为一个蓝色链接。 浏览器将始终默认为最精确的输入功能。


谢谢,这会很方便!然而,我尝试在Chrome的移动模式下执行它,但并没有成功 - 当我“轻点”它时,它仍然变成了蓝色... - Simon Ferndriger
1
@SimonFerndriger 这只是Chrome开发工具的限制。请尝试在实际的触摸设备上打开链接。 - Jason
1
在iPad Chrome / Safari上完美运行,与OS X Chrome / Safari相比。感谢!终于有了您对于这种小型视觉问题想要的优雅解决方案。 - rakaloof
3
我刚在我真正运行iOS 11的iPhone 6S上尝试了这个,链接变成了蓝色。 - Lukas Petr
1
还尝试在运行Android 8.1.0和Chrome 68.0.3440.91的Nexus 5X上进行了测试,链接变成了蓝色。 - Trevin Avery
显示剩余6条评论

23

根据 Jason 的回答,我们可以使用纯 CSS 媒体查询来仅针对不支持悬停的设备进行处理。我们也可以像类似问题中 moogal 的答案 中那样仅针对支持悬停的设备进行处理,使用@media not all and (hover: none)。看起来很奇怪,但它确实有效。

我将其制作为 Sass Mixin 以便更容易使用:

@mixin hover-supported {
    @media not all and (hover: none) {
        &:hover {
            @content;
        }
    }
}

更新于2019-05-15:我推荐从Medium获取的这篇文章,它介绍了我们可以使用CSS针对不同设备的所有方法。基本上是这些媒体规则的组合,用于特定的目标:

@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 */
}

特定目标的示例:

@media (hover: none) and (pointer: coarse) {
    /* Smartphones and touchscreens */
}

@media (hover: hover) and (pointer: fine) {
    /* Desktops with mouse */
}

我喜欢 mixin,在这里我将展示如何使用我的 hover mixin 只针对支持它的设备:

@mixin on-hover {
    @media (hover: hover) and (pointer: fine) {
        &:hover {
            @content;
        }
    }
}

button {
    @include on-hover {
        color: blue;
    }
}

完全不起作用。在支持鼠标悬停的mixin的媒体查询中,你有hover:none吗?需要像这里其他答案所述的那样使用pointer:coarse。 - Allan Nienhuis
你有试过它吗?还是只是因为看起来奇怪就说它不起作用?这就是为什么我写了“看起来奇怪”...而且它对我来说没有指针也可以工作:coarse。 - Calsal
我在我的OnePlus 5T上使用CSS方法时遇到了一些不一致的结果(在Chrome和Firefix中都是如此)。这在iOS上运行良好,但我无法让它在OnePlus上正常工作。我使用any-pointer和any-hover进行了彻底的测试。 - Zeth

17

我遇到了相同的问题(在我的情况下是与三星移动浏览器),因此我偶然发现了这个问题。

感谢Calsal的回答,我找到了一个我认为会排除几乎所有桌面浏览器的东西,因为它似乎被我尝试过的移动浏览器所识别(参见编译表中的屏幕截图:CSS指针功能检测表)。

MDN Web Docs的声明如下:

指针CSS @media 功能可用于基于用户的主要输入机制(是否为指针设备)应用样式,以及该设备的精度如何。

我发现的是,pointer:coarse是附加表中所有桌面浏览器都不知道的内容,但是同一表中的所有移动浏览器都知道。 这似乎是最有效的选择,因为所有其他指针关键字值都会产生不一致的结果。

因此,您可以像Calsal描述的那样构建一个媒体查询,但稍作修改。 它使用了一种相反的逻辑来排除所有触摸设备。

Sass mixin:

@mixin hover-supported {    
    /* 
     * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer 
     * coarse: The primary input mechanism includes a pointing device of limited accuracy.
     */
    @media not all and (pointer: coarse) {
        &:hover {
            @content;
        }
    }
}

a {
    color:green;
    border-color:blue;

    @include hover-supported() {
        color:blue;
        border-color:green;
    }
}

编译后的 CSS:

a {
  color: green;
  border-color: blue;
}
@media not all and (pointer: coarse) {
  a:hover {
    color: blue;
    border-color: green;
  }
}

在我调研了这个问题之后,我创建了这个Gist中也有描述。

用于经验研究的Codepen

更新(2018): 截至撰写本更新,即2018年8月23日,并由@DmitriPavlutin指出,此技术似乎不再适用于Firefox桌面版。

更新(2021): 有人向我指出,它似乎在Firefox 87上运行。


谢谢@DmitriPavlutin,根据表格(编译时)应该是这样的,但我可以再看一下。 - ProgrammerPer
@DmitriPavlutin 您说得很对。我会更新答案。 - ProgrammerPer
1
你的意思是,如果使用专为触摸设备设计的工具,在 Firefox 桌面版上无法正常工作?但在我尝试的所有场景中,它都能够运行良好。 - Fuzzee Lowgeek
@FuzzeeLowgeek 听起来不错!我上次检查是在2018年8月23日,所以如果你能确定这一点,也许我可以再次更新答案? - ProgrammerPer

7

这确实可以解决问题。我以前从未听说过这个。这应该在哪个浏览器版本中起作用? - Simon Ferndriger
3
以下是需要翻译的内容:@SimonFerndriger http://caniuse.com/#feat=css-media-interaction and some more info: https://dev.opera.com/articles/media-features/这条信息提供了两个网站链接。第一个链接是“caniuse.com”的CSS媒体交互功能页面,第二个链接是关于媒体特性的文章。 - Sceptic
好的...它在浏览器方面的支持并不广泛。 - Giacomo Paita
太棒了!谢谢你提供如此方便的解决方案。我希望它能被添加到W3C标准中。 - GProst
很高兴能帮助到您 ^^ - Marouen Mhiri

6

我目前正在处理一个类似的问题。

我立即想到了两个主要的选择:(1)用户字符串检查,或者(2)使用不同的URL维护单独的移动页面,并让用户选择哪个更好。

  1. 如果您能够使用像PHP或Ruby这样的互联网“胶带”语言,则可以检查请求页面的设备的用户字符串,并简单地提供相同的内容,但使用<link rel="mobile.css" />而不是普通样式。

用户字符串包含有关浏览器、渲染器、操作系统等的标识信息。由您决定哪些设备是“触摸”设备和非触摸设备。您可以在某个地方找到此信息并将其映射到您的系统中。

A. 如果您被允许忽略旧浏览器,则只需向普通的非移动css添加一条规则,即: 编辑:Erk。经过一些实验,我发现下面的规则还会禁用WebKit浏览器中的链接跟踪功能,除了导致美学效果被禁用之外-请参见http://jsfiddle.net/3nkcdeao/
因此,您必须更加谨慎地选择如何修改移动情况下的规则,而不是我在这里展示的内容,但它可能是一个有用的起点:

* { 
    pointer-events: none !important; /* only use !important if you have to */
}

作为附注,如果在父元素上禁用 pointer-events,然后在子元素上明确启用它们,当子元素进入:hover时,会重新激活父元素上的任何悬停效果。
请参见http://jsfiddle.net/38Lookhp/5/ B. 如果您支持旧版Web渲染器,您将需要进行一些更多的工作,例如删除任何在:hover期间设置特殊样式的规则。为节省时间,您可能只需构建一个自动复制+sed命令,然后在标准样式表上运行它以创建移动版本。这将允许您仅编写/更新标准代码,并清除使用:hover的任何样式规则,以用于页面的移动版本。
2. 或者,只需让用户知道您有一个专门为移动设备设计的m.website.com网站,而不是website.com网站。虽然子域名是最常见的方法,但您还可以对给定URL进行其他可预测的修改,以允许移动用户访问修改后的页面。在那个阶段,您需要确保他们不必每次导航到站点的另一部分时都要修改URL。
再次强调,您可能只需向样式表添加一个或两个额外的规则,或者被迫使用sed或类似实用程序进行一些稍微复杂的操作。最简单的方法可能是将:not应用于样式规则,例如
2. 您实际上可以结合前两者(如果您怀疑用户进入了页面的错误版本),可以建议他们切换到/退出移动类型显示,或者只是在某个地方放置链接,允许用户来回切换。正如先前所述,@media查询也可能是确定正在使用何种设备访问的东西。
3. 如果您知道哪些设备是“触摸”设备,哪些不是,并且愿意使用jQuery解决方案,您可能会发现在触摸屏幕设备上不忽略CSS悬停有所帮助。

pointer-events - 真是太棒了!这正是我在寻找的,非常感谢! - Simon Ferndriger
1
@SimonFerndriger 希望这对你以后有所帮助。请注意,到目前为止,<a>链接可能无法在pointer-events:none下访问。希望这种情况能够改变,或者CSS 3.x或4.0+将具有pointer-events:navigation-only或类似功能。 - Seldom 'Where's Monica' Needy

5
您可以使用 Modernizr JS(也可以参考这个 StackOverflow 答案),或者编写自定义的 JS 函数:
function is_touch_device() {
 return 'ontouchstart' in window        // works on most browsers 
  || navigator.maxTouchPoints;       // works on IE10/11 and Surface
};

if ( is_touch_device() ) {
  $('html').addClass('touch');
} else {
  $('html').addClass('no-touch');
} 

为了检测浏览器是否支持触摸事件,并将常规的CSS属性分配给具有html.no-touch类的元素,可以遍历该元素,如下所示:

html.touch a {
    width: 480px;
}

/* FOR THE DESKTOP, SET THE HOVER STATE */
html.no-touch a:hover {
   width: auto;
   color:blue;
   border-color:green;
}

那也是由@blade建议的 - 但是,使用html标签而不是body标签可能会使它更易于阅读。这实际上是我目前正在使用的解决方案。无论如何,谢谢。 - Simon Ferndriger

3
经过查看之前的答案,以下方法对我有效。 这个hover只适用于拥有hover功能的桌面设备。如果您有任何其他与此类/ID相关的hover代码,请删除它们。
@media (hover: hover) {
  .toggle-label:hover {
     background-color: #69c9ff;
     color: #ffffff;
  }
}

3
为了使当前答案在 IE11 中也能正常工作(如果你仍在支持IE11):

@media (hover: hover) and (pointer: fine), only screen and (-ms-high-contrast:active), (-ms-high-contrast:none) {
  a:hover { color: red; }
}
<a href="#" >Some Link</a>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接