禁用移动浏览器上的悬停效果

IT技术 javascript hover touch
2021-01-15 11:28:04

我正在编写一个网站,该网站旨在用于台式机和平板电脑。当从桌面访问它时,我希望屏幕的可点击区域点亮:hover效果(不同的背景颜色等)。平板电脑没有鼠标,所以我不想要任何悬停效果。

问题是,当我点击平板电脑上的某些东西时,浏览器显然有某种“隐形鼠标光标”,它会移动到我点击的位置,然后将其留在那里——所以我刚刚点击的东西会亮起悬停效果,直到我点击其他东西。

如何在使用鼠标时获得悬停效果,而在使用触摸屏时抑制它们?

如果有人想提出建议,我不想使用用户代理嗅探。同一个设备可以同时具有触摸屏和鼠标(今天可能不那么常见,但将来会更多)。我对设备不感兴趣,我对它目前的使用方式感兴趣:鼠标或触摸屏。

我已经尝试挂钩touchstarttouchmovetouchend事件,并呼吁preventDefault()对所有的人,这确实抑制“隐形鼠标”的一些时间; 但是如果我在两个不同的元素之间快速来回轻敲,轻敲几下后,它会开始移动“鼠标光标”并无论如何都会点亮悬停效果 - 就像我preventDefault并不总是受到尊重。除非必要,否则我不会让你厌烦细节——我什至不确定这是正确的方法;如果有人有更简单的解决方法,我会全力以赴。


编辑:这可以用 bog-standard CSS 进行复制:hover,但这里有一个快速复制供参考。

<style>
  .box { border: 1px solid black; width: 150px; height: 150px; }
  .box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>

如果您将鼠标悬停在任何一个框上,它将获得我想要的蓝色背景。但是如果你点击其中一个框,它也会得到蓝色背景,这是我试图阻止的事情。

我还在此处发布了一个示例该示例执行上述操作并挂钩 jQuery 的鼠标事件。您可以使用它来查看点击事件也将触发mouseenter,mousemovemouseleave

6个回答

我从您的问题中得知您的悬停效果会更改您页面的内容。在这种情况下,我的建议是:

  • touchstart上添加悬停效果mouseenter
  • 移除mouseleavetouchmove上的悬停效果click

或者,您可以编辑没有内容更改的页面。

背景

为了模拟鼠标,如果用户在触摸屏(如 iPad)上触摸和释放手指,Webkit mobile 等浏览器会触发以下事件(来源:html5rocks.com 上的Touch And Mouse):

  1. touchstart
  2. touchmove
  3. touchend
  4. 300 毫秒延迟,浏览器确保这是单击而不是双击
  5. mouseover
  6. mouseenter
    • 注意:如果mouseover,mouseentermousemove事件更改了页面内容,则永远不会触发以下事件。
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

似乎不可能简单地告诉网络浏览器跳过鼠标事件。

更糟糕的是,如果鼠标悬停事件更改了页面内容,则永远不会触发单击事件,如Safari Web 内容指南 - 处理事件中所述,特别是单指事件中的图 6.4 究竟什么是“内容更改”,将取决于浏览器和版本。我发现对于 iOS 7.0,背景颜色的变化不是(或不再是?)内容变化。

解决方案说明

回顾一下:

  • touchstart上添加悬停效果mouseenter
  • 移除mouseleavetouchmove上的悬停效果click

请注意,没有对touchend!

这显然适用于鼠标事件:mouseentermouseleave(略有改善的版本mouseovermouseout)被解雇,并添加和删除悬停。

如果用户确实使用了click链接,则悬停效果也会被移除。这可确保在用户按下 Web 浏览器中的后退按钮时将其删除。

这也适用于触摸事件:在 touchstart 上添加了悬停效果。它在 touchend 上“不”被删除。它在 上再次添加mouseenter,由于这不会导致内容更改(已添加),因此click也会触发事件,并且无需用户再次单击即可跟踪链接!

浏览器在touchstart事件和click实际之间的 300 毫秒延迟实际上得到了很好的利用,因为悬停效果将在这段短时间内显示出来。

如果用户决定取消点击,手指的移动会像往常一样。通常,这是一个问题,因为没有mouseleave触发任何事件,并且悬停效果仍然存在。幸运的是,这可以通过移除 上的悬停效果轻松解决touchmove

而已!

请注意,可以删除 300 毫秒延迟,例如使用FastClick 库,但这超出了此问题的范围。

替代解决方案

我发现以下替代方案存在以下问题:

  • 浏览器检测:极易出错。假设设备具有鼠标或触摸功能,而当触摸显示器激增时,两者的组合将变得越来越普遍。
  • CSS 媒体检测:我所知道的唯一纯 CSS 解决方案。仍然容易出错,并且仍然假设设备具有鼠标或触摸功能,而两者都是可能的。
  • 在 中模拟点击事件touchend这将错误地跟随链接,即使用户只想滚动或缩放,而无意实际点击链接。
  • 使用变量抑制鼠标事件:这会设置一个变量touchend,用作后续鼠标事件中的 if 条件,以防止该时间点的状态更改。该变量在点击事件中被重置。请参阅本页上 Walter Roman 的回答。如果您真的不想在触摸界面上出现悬停效果,这是一个不错的解决方案。不幸的是,如果 atouchend由于其他原因被触发并且没有触发点击事件(例如用户滚动或缩放),并且随后尝试使用鼠标跟踪链接(即在具有鼠标和触摸界面的设备上),则这不起作用)。

进一步阅读

伟大的答案,但在大多数情况下,我建议使用touchend 替代touchstart在大多数情况下,立即避免触发动作,当用户试图向上或滑动,向下滚动页面。仍然会发生,但不太可能分散注意力或混淆(假设它是无害的,比如显示标签,而不是用户需要知道发生的事情)
2021-04-08 11:28:04
很好的答案。我用你的小提琴开始创建我自己的解决方案。然而,有时需要触摸端。(当 touchmove 不运行时——即当用户离开文档本身时......)
2021-04-10 11:28:04

如何在使用鼠标时获得悬停效果,而在使用触摸屏时抑制它们?

也许不要认为它是抑制触摸屏的悬停效果,而是为鼠标事件添加悬停效果?

如果您想:hover在 CSS 中保留效果,您可以为不同的媒体指定不同的样式:

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

不幸的是,有很多移动设备忽略了这一点,只使用屏幕规则。幸运的是,许多较新的移动/平板电脑浏览器确实支持一些更高级的媒体查询:

@media screen and (max-width:800px) { /* non-hover styles here */ }

因此,即使忽略“屏幕”或“手持设备”部分,“最大宽度”也会为您解决问题。你可以假设屏幕小于 800 像素的任何东西都必须是平板电脑或手机,而不是使用悬停效果。对于在低分辨率设备上使用鼠标的极少数用户,他们不会看到悬停效果,但您的网站在其他情况下会很好。

进一步阅读媒体查询?网上有很多关于这个的文章 - 这里是一篇:http : //www.alistapart.com/articles/return-of-the-mobile-stylesheet

如果您将悬停效果从 CSS 中移出并使用 JavaScript 应用它们,那么您可以专门绑定到鼠标事件,和/或您可以再次仅根据屏幕大小做出一些假设,最坏的“问题”是一些使用鼠标的用户会错过悬停效果。

好吧,一旦浏览器开发人员在具有触摸和鼠标的设备上拥有更多用户,他们可能会更有动力使检测更加一致。但现在?我会根据屏幕大小进行猜测,但其他答案中还有其他建议。抱歉,我帮不上忙了。
2021-03-14 11:28:04
至于媒体查询,当 Windows 8 出现并且 PC 拥有触摸屏鼠标时会发生什么?如果用户用鼠标悬停,他们会看到鼠标光标,我想要悬停效果;如果他们点击触摸屏,我不想要悬停效果。但是我还没有找到一种可靠的方法来检测触摸和鼠标之间的差异。
2021-03-19 11:28:04
我只想为鼠标事件添加悬停效果。但触摸事件模拟鼠标事件。如果我挂钩触摸事件并调用preventDefault(),有时会抑制鼠标事件,但正如我在我的问题中所说,它不可靠。
2021-03-26 11:28:04

我为最近的一个项目编写了以下 JS,这是一个桌面/移动/平板电脑站点,具有不应在触摸时出现的悬停效果。

mobileNoHoverState下面module有一个变量preventMouseover(最初声明为false),true当用户touchstart在元素上触发事件设置为$target

preventMouseover然后在触发事件false重新设置mouseover为,如果用户同时使用触摸屏和鼠标,则允许站点按预期工作。

我们知道这mouseovertouchstart因为它们在 内声明的顺序而被触发init

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

然后该module被进一步实例化:

mobileNoHoverState.init();
@Redtopia 感谢您的提醒!我已经用被遗漏的代码更新了答案。
2021-03-18 11:28:04
“每当鼠标悬停事件被触发时,preventMouseover 就会被设置回 true,如果用户同时使用他们的触摸屏和鼠标,这允许网站按预期工作。” - 你的代码没有这样做。我错过了什么吗?
2021-03-19 11:28:04
@nacholibre 正确使用分号
2021-03-29 11:28:04
函数声明后不需要分号
2021-04-08 11:28:04

我自己真的很想要一个纯粹的css解决方案,因为在我的所有观点周围撒上一个重量级的 javascript 解决方案似乎是一个令人不快的选择。终于找到了@media.hover查询,它可以检测“主要输入机制是否允许用户将鼠标悬停在元素上”。这避免了“悬停”更多是模拟动作而不是输入设备的直接功能的触摸设备。

例如,如果我有一个链接:

<a href="/" class="link">Home</a>

然后我可以安全地将其样式设置为仅:hover当设备轻松支持它时css

@media (hover: hover) {
  .link:hover { /* hover styles */ }
}

虽然大多数现代浏览器都支持交互媒体功能查询,但一些流行的浏览器(如 IE 和 Firefox)不支持。就我而言,这很好用,因为我只打算在桌面上支持 Chrome,在移动设备上支持 Chrome 和 Safari。

当您在 Chrome devtools 中模拟设备时似乎可以工作的不错的解决方案。但它仍然不适用于 Chrome Android :-(
2021-03-20 11:28:04
我认为这是最方便和正确的解决方案。希望它很快就会被添加到 W3C 标准中。
2021-03-30 11:28:04

我的解决方案是将悬停活动 css 类添加到 HTML 标记,并在所有 CSS 选择器的开头使用 :hover 并在第一个 touchstart 事件中删除该类。

http://codepen.io/Bnaya/pen/EoJlb

JS:

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML:

<html class="hover-active">

CSS:

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}
您可以测试'ontouchstart' in window. 例如if ('ontouchstart' in window) document.querySelector('html').classList.remove('hover-active');
2021-03-25 11:28:04
@YuvalA。我正在等待触摸事件的原因是有具有指针和触摸支持的设备,并且用户正在使用指针,我不想删除悬停。用户可能会先使用触摸然后使用指针,但我对此无能为力
2021-04-12 11:28:04