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

2022-01-22 00:00:00 hover javascript touch

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

I'm writing a Web site that's meant to be used from both desktops and tablets. When it's being visited from a desktop, I want the clickable areas of the screen to light up with :hover effects (different background color, etc.) With a tablet, there's no mouse, so I don't want any hover effects.

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

The problem is, when I tap something on the tablet, the browser evidently has some kind of "invisible mouse cursor" that it moves to the location I tapped, and then leaves it there -- so the thing I just tapped lights up with a hover effect until I tap something else.

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

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

In case someone was thinking of suggesting it, I don't want to use user-agent sniffing. The same device could have both a touchscreen and a mouse (maybe not so common today, but much more so in the future). I'm not interested in the device, I'm interested in how it's currently being used: mouse or touchscreen.

我已经尝试挂钩 touchstarttouchmovetouchend 事件并在所有事件上调用 preventDefault()其中,有时确实会抑制隐形鼠标光标";但是如果我在两个不同的元素之间来回快速点击,点击几下后它会开始移动鼠标光标"并点亮悬停效果——就像我的 preventDefault 并不总是荣幸.除非必要,否则我不会让你厌烦细节——我什至不确定这是正确的方法;如果有人有更简单的解决方法,我会全力以赴.

I already tried hooking the touchstart, touchmove, and touchend events and calling preventDefault() on all of them, which does suppress the "invisible mouse cursor" some of the time; but if I tap rapidly back and forth between two different elements, after a few taps it will start moving the "mouse cursor" and lighting up the hover effects anyway -- it's like my preventDefault isn't always honored. I won't bore you with the details unless necessary -- I'm not even sure that's the right approach to take; if anyone has a simpler fix, I'm all ears.

这可以用沼泽标准 CSS :hover 重现,但这里有一个快速复制供参考.

This can be reproduced with bog-standard CSS :hover, but here's a quick repro for reference.

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

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

If you mouse over either of the boxes, it will get a blue background, which I want. But if you tap on either of the boxes, it will also get a blue background, which is the thing I'm trying to prevent.

我还发布了一个示例 here,它执行上述操作并与 jQuery 挂钩鼠标事件.您可以使用它来查看点击事件也会触发 mouseentermousemovemouseleave.

I've also posted a sample here that does the above and also hooks jQuery's mouse events. You can use it to see that tap events will also fire mouseenter, mousemove and mouseleave.

推荐答案

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

I take it from your question that your hover effect changes the content of your page. In that case, my advice is to:

  • touchstartmouseenter 上添加悬停效果.
  • 移除 mouseleavetouchmoveclick 上的悬停效果.
  • Add hover effects on touchstart and mouseenter.
  • Remove hover effects on mouseleave, touchmove and click.

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

Alternatively, you can edit your page that there is no content change.

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

In order to simulate a mouse, browsers such as Webkit mobile fire the following events if a user touches and releases a finger on touch screen (like iPad) (source: Touch And Mouse on html5rocks.com):

  1. touchstart
  2. touchmove
  3. touchend
  4. 300 毫秒延迟,浏览器确保这是单击而不是双击
  5. 鼠标悬停
  6. 鼠标输入
    • 注意:如果 mouseovermouseentermousemove 事件改变了页面内容,则以下事件为从未被解雇.
  1. touchstart
  2. touchmove
  3. touchend
  4. 300ms delay, where the browser makes sure this is a single tap, not a double tap
  5. mouseover
  6. mouseenter
    • Note: If a mouseover, mouseenter or mousemove event changes the page content, the following events are never fired.

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

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

What's worse, if a mouseover event changes the page content, the click event is never fired, as explained on Safari Web Content Guide - Handling Events, in particular figure 6.4 in One-Finger Events. What exactly a "content change" is, will depend on browser and version. I've found that for iOS 7.0, a change in background color is not (or no longer?) a content change.

回顾一下:

  • touchstartmouseenter 上添加悬停效果.
  • 移除 mouseleavetouchmoveclick 上的悬停效果.
  • Add hover effects on touchstart and mouseenter.
  • Remove hover effects on mouseleave, touchmove and click.

注意 touchend 上没有任何动作!

Note that there is no action on touchend!

这显然适用于鼠标事件:mouseentermouseleave(mouseovermouseout 的略微改进版本)被触发,然后添加和删除悬停.

This clearly works for mouse events: mouseenter and mouseleave (slightly improved versions of mouseover and mouseout) are fired, and add and remove the hover.

如果用户实际点击链接,悬停效果也会被移除.这确保如果用户在 Web 浏览器中按下后退按钮,它会被删除.

If the user actually clicks a link, the hover effect is also removed. This ensure that it is removed if the user presses the back button in the web browser.

这也适用于触摸事件:在 touchstart 时添加了悬停效果.它在 touchend 上'''not''' 被删除.它在 mouseenter 上再次添加,并且由于这不会导致内容更改(它已经添加),所以 click 事件也被触发,并且不需要链接就可以跟随供用户再次点击!

This also works for touch events: on touchstart the hover effect is added. It is '''not''' removed on touchend. It is added again on mouseenter, and since this causes no content changes (it was already added), the click event is also fired, and the link is followed without the need for the user to click again!

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

The 300ms delay that a browser has between a touchstart event and click is actually put in good use because the hover effect will be shown during this short time.

如果用户决定取消点击,手指的移动将照常执行.通常,这是一个问题,因为没有触发 mouseleave 事件,并且悬停效果仍然存在.值得庆幸的是,可以通过移除 touchmove 上的悬停效果轻松解决此问题.

If the user decides to cancel the click, a move of the finger will do so just as normal. Normally, this is a problem since no mouseleave event is fired, and the hover effect remains in place. Thankfully, this can easily be fixed by removing the hover effect on touchmove.

就是这样!

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

Note that it is possible to remove the 300ms delay, for example using the FastClick library, but this is out of scope for this question.

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

I've found the following problems with the following alternatives:

  • 浏览器检测:极易出错.假设设备具有鼠标或触控功能,而当触控显示器激增时,两者的结合将变得越来越普遍.
  • CSS 媒体检测:我所知道的唯一纯 CSS 解决方案.仍然容易出错,并且仍然假定设备具有鼠标或触摸功能,而两者都是可能的.
  • touchend 中模拟点击事件: 这将错误地跟随链接,即使用户只想滚动或缩放,而没有实际点击链接的意图.
  • 使用变量来抑制鼠标事件:这会在 touchend 中设置一个变量,该变量用作后续鼠标事件中的 if 条件,以防止此时状态发生变化及时.变量在点击事件中被重置.请参阅此页面上的 Walter Roman 的回答.如果您真的不希望在触摸界面上产生悬停效果,这是一个不错的解决方案.不幸的是,如果 touchend 因其他原因被触发并且没有触发点击事件(例如用户滚动或缩放),并且随后尝试用鼠标跟随链接(即在具有鼠标和触摸界面的设备).
  • browser detection: Extremely prone to errors. Assumes that a device has either mouse or touch, while a combination of both will become more and more common when touch displays prolifirate.
  • CSS media detection: The only CSS-only solution I'm aware of. Still prone to errors, and still assumes that a device has either mouse or touch, while both are possible.
  • Emulate the click event in touchend: This will incorrectly follow the link, even if the user only wanted to scroll or zoom, without the intention of actually clicking the link.
  • Use a variable to suppress mouse events: This set a variable in touchend that is used as a if-condition in subsequent mouse events to prevents state changes at that point in time. The variable is reset in the click event. See Walter Roman's answer on this page. This is a decent solution if you really don't want a hover effect on touch interfaces. Unfortunately, this does not work if a touchend is fired for another reason and no click event is fired (e.g. the user scrolled or zoomed), and is subsequently trying to following the link with a mouse (i.e on a device with both mouse and touch interface).
  • http://jsfiddle.net/macfreek/24Z5M/.在此沙盒中自行测试上述解决方案.
  • http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser.相同的答案,但有更多背景知识.
  • https://www.html5rocks.com/en/mobile/touchandmouse/.html5rocks.com 上有关触摸和鼠标的一般背景文章.
  • https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html.Safari Web 内容指南 - 处理事件.具体参见图 6.4,它解释了在 mouseovermousemove 事件期间内容更改后不会触发更多事件.
  • http://jsfiddle.net/macfreek/24Z5M/. Test the above solution for yourself in this sandbox.
  • http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser. This same answer, with a bit more background.
  • https://www.html5rocks.com/en/mobile/touchandmouse/. Great background article on html5rocks.com about touch and mouse in general.
  • https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html. Safari Web Content Guide - Handling Events. See in particular figure 6.4, which explains that no further events are fired after a content change during a mouseover or mousemove event.

相关文章