Javascript 禁用移动浏览器上的悬停效果
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8291517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Disable hover effects on mobile browsers
提问by Joe White
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.
我正在编写一个网站,该网站旨在用于台式机和平板电脑。当从桌面访问它时,我希望屏幕的可点击区域点亮:hover
效果(不同的背景颜色等)。平板电脑没有鼠标,所以我不想要任何悬停效果。
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.
问题是,当我点击平板电脑上的某些东西时,浏览器显然有某种“隐形鼠标光标”,它会移动到我点击的位置,然后将其留在那里——所以我刚刚点击的东西会亮起悬停效果,直到我点击其他东西。
How can I get the hover effects when I'm using the mouse, but suppress them when I'm using the touchscreen?
如何在使用鼠标时获得悬停效果,而在使用触摸屏时抑制它们?
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.
如果有人想提出建议,我不想使用用户代理嗅探。同一个设备可以同时具有触摸屏和鼠标(今天可能不那么常见,但将来会更多)。我对设备不感兴趣,我对它目前的使用方式感兴趣:鼠标或触摸屏。
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.
我已经尝试挂钩touchstart
,touchmove
和touchend
事件,并呼吁preventDefault()
对所有的人,这确实抑制“隐形鼠标”的一些时间; 但是如果我在两个不同的元素之间快速来回轻敲,轻敲几下后,它会开始移动“鼠标光标”并无论如何都会点亮悬停效果——就像我preventDefault
并不总是受到尊重。除非必要,否则我不会让你厌烦细节——我什至不确定这是正确的方法;如果有人有更简单的解决方法,我会全力以赴。
Edit:This can be reproduced with bog-standard CSS :hover
, but here's a quick repro for reference.
编辑:这可以用 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>
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.
如果您将鼠标悬停在任何一个框上,它将获得我想要的蓝色背景。但是如果你点击其中一个框,它也会得到蓝色背景,这是我试图阻止的事情。
I've also posted a sample herethat 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
.
我还在此处发布了一个示例,该示例执行上述操作并挂钩 jQuery 的鼠标事件。您可以使用它来查看点击事件也将触发mouseenter
,mousemove
和mouseleave
。
回答by MacFreek
I take it from your question that your hover effect changes the content of your page. In that case, my advice is to:
我从您的问题中得知您的悬停效果会更改您页面的内容。在这种情况下,我的建议是:
- Add hover effects on
touchstart
andmouseenter
. - Remove hover effects on
mouseleave
,touchmove
andclick
.
- 在
touchstart
和上添加悬停效果mouseenter
。 - 移除
mouseleave
、touchmove
和上的悬停效果click
。
Alternatively, you can edit your page that there is no content change.
或者,您可以编辑没有内容更改的页面。
Background
背景
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 Mouseon html5rocks.com):
为了模拟鼠标,如果用户在触摸屏(如 iPad)上触摸和释放手指,Webkit mobile 等浏览器会触发以下事件(来源:html5rocks.com 上的Touch And Mouse):
touchstart
touchmove
touchend
- 300ms delay, where the browser makes sure this is a single tap, not a double tap
mouseover
mouseenter
- Note: If a
mouseover
,mouseenter
ormousemove
event changes the page content, the following events are never fired.
- Note: If a
mousemove
mousedown
mouseup
click
touchstart
touchmove
touchend
- 300 毫秒延迟,浏览器确保这是单击而不是双击
mouseover
mouseenter
- 注意:如果
mouseover
,mouseenter
或mousemove
事件更改了页面内容,则永远不会触发以下事件。
- 注意:如果
mousemove
mousedown
mouseup
click
It does not seem possible to simply tell the webbrowser to skip the mouse events.
似乎不可能简单地告诉网络浏览器跳过鼠标事件。
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.
更糟糕的是,如果鼠标悬停事件更改了页面内容,则永远不会触发单击事件,如Safari Web 内容指南 - 处理事件中所述,特别是单指事件中的图 6.4 。“内容更改”究竟是什么,将取决于浏览器和版本。我发现对于 iOS 7.0,背景颜色的变化不是(或不再是?)内容变化。
Solution Explained
解决方案说明
To recap:
回顾一下:
- Add hover effects on
touchstart
andmouseenter
. - Remove hover effects on
mouseleave
,touchmove
andclick
.
- 在
touchstart
和上添加悬停效果mouseenter
。 - 移除
mouseleave
、touchmove
和上的悬停效果click
。
Note that there is no action on touchend
!
请注意,没有对touchend
!
This clearly works for mouse events: mouseenter
and mouseleave
(slightly improved versions of mouseover
and mouseout
) are fired, and add and remove the hover.
这显然适用于鼠标事件:mouseenter
和mouseleave
(略有改善的版本mouseover
和mouseout
)被解雇,并添加和删除悬停。
If the user actually click
s 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.
如果用户确实使用了click
链接,则悬停效果也会被移除。这可确保在用户按下 Web 浏览器中的后退按钮时将其删除。
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 上添加了悬停效果。它在 touchend 上“不”被删除。它在 上再次添加mouseenter
,并且由于这不会导致内容更改(已添加),因此click
也会触发该事件,并且无需用户再次单击即可跟踪链接!
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.
浏览器在touchstart
事件和click
实际之间的 300 毫秒延迟实际上得到了很好的利用,因为悬停效果将在这么短的时间内显示出来。
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
.
如果用户决定取消点击,手指的移动会像往常一样。通常,这是一个问题,因为没有mouseleave
触发任何事件,并且悬停效果仍然存在。幸运的是,这可以通过移除 上的悬停效果轻松解决touchmove
。
That's it!
就是这样!
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.
请注意,可以删除 300 毫秒延迟,例如使用FastClick 库,但这超出了此问题的范围。
Alternative Solutions
替代解决方案
I've found the following problems with the following alternatives:
我发现以下替代方案存在以下问题:
- 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 atouchend
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).
- 浏览器检测:极易出错。假设设备具有鼠标或触摸功能,而当触摸显示器激增时,两者的组合将变得越来越普遍。
- CSS 媒体检测:我所知道的唯一纯 CSS 解决方案。仍然容易出错,并且仍然假设设备具有鼠标或触摸功能,而两者都是可能的。
- 在 中模拟点击事件
touchend
:这将错误地跟随链接,即使用户只想滚动或缩放,而无意实际点击链接。 - 使用变量来抑制鼠标事件:这会设置一个变量
touchend
,用作后续鼠标事件中的 if 条件,以防止该时间点的状态更改。该变量在点击事件中被重置。请参阅本页上 Walter Roman 的回答。如果您真的不想在触摸界面上出现悬停效果,这是一个不错的解决方案。不幸的是,如果 atouchend
由于其他原因被触发并且没有触发点击事件(例如用户滚动或缩放),并且随后尝试使用鼠标跟踪链接(即在具有鼠标和触摸界面的设备上),则这不起作用)。
Further Reading
进一步阅读
- 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
ormousemove
event.
- 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,它解释了在
mouseover
或mousemove
事件期间内容更改后不会触发其他事件。
回答by nnnnnn
How can I get the hover effects when I'm using the mouse, but suppress them when I'm using the touchscreen?
如何在使用鼠标时获得悬停效果,而在使用触摸屏时抑制它们?
Maybe don't think of it so much as suppressing hover effects for touchscreens, but as adding hover effects for mouse events?
也许不要认为它是抑制触摸屏的悬停效果,而是为鼠标事件添加悬停效果?
If you want to keep the :hover
effects in your CSS you could specify different styles for different media:
如果你想:hover
在你的 CSS 中保留效果,你可以为不同的媒体指定不同的样式:
@media screen { /* hover styles here */ }
@media handheld { /* non-hover styles here */ }
Except that unfortunately there are plenty of mobile devices that ignore this and just use the screen rules. Fortunately a lot of newer mobile/tablet browsers do support some fancier media queries:
不幸的是,有很多移动设备忽略了这一点,只使用屏幕规则。幸运的是,许多较新的移动/平板电脑浏览器确实支持一些更高级的媒体查询:
@media screen and (max-width:800px) { /* non-hover styles here */ }
So even if the "screen" or "handheld" part is ignored the "max-width" will do the trick for you. You could just assume that anything with a screen smaller than 800 pixels must be a tablet or phone, and not use hover effects. For the rare users who are using a mouse on a low resolution device they wouldn't see the hover effects but your site would be fine otherwise.
因此,即使忽略“屏幕”或“手持设备”部分,“最大宽度”也会为您解决问题。你可以假设屏幕小于 800 像素的任何东西都必须是平板电脑或手机,而不是使用悬停效果。对于在低分辨率设备上使用鼠标的极少数用户,他们不会看到悬停效果,但您的网站在其他情况下会很好。
Further reading on media queries? There are plenty of articles about this online - here is one: http://www.alistapart.com/articles/return-of-the-mobile-stylesheet
进一步阅读媒体查询?网上有很多关于这个的文章 - 这里是一篇:http: //www.alistapart.com/articles/return-of-the-mobile-stylesheet
If you shift the hover effects out of your CSS and apply them with JavaScript then you could bind specifically to mouse events, and/or again you could just make some assumptions just based on screen size with the worst-case "problem" being that some user who is using a mouse misses out on the hover effects.
如果您将悬停效果从 CSS 中移出并使用 JavaScript 应用它们,那么您可以专门绑定到鼠标事件,和/或您可以再次仅根据屏幕大小做出一些假设,最坏的“问题”是一些使用鼠标的用户会错过悬停效果。
回答by Walter Roman
I wrote the following JS for a recent project, which was a desktop/mobile/tablet site that has hover effects that shouldn't appear on-touch.
我为最近的一个项目编写了以下 JS,这是一个桌面/移动/平板电脑站点,具有不应在触摸时出现的悬停效果。
The mobileNoHoverState
module below has a variable preventMouseover
(initially declared as false
), that is set to true
when a user fires the touchstart
event on an element, $target
.
mobileNoHoverState
下面的模块有一个变量preventMouseover
(最初声明为false
),true
当用户touchstart
在元素上触发事件时设置为$target
。
preventMouseover
is then being set back to false
whenever the mouseover
event is fired, which allows the site to work as intended if a user is using both their touchscreen and mouse.
preventMouseover
然后false
在mouseover
事件被触发时被设置回,如果用户同时使用他们的触摸屏和鼠标,这允许站点按预期工作。
We know that mouseover
is being triggered after touchstart
because of the order that they are being declared within init
.
我们知道由于它们在 中声明的顺序而mouseover
被触发。touchstart
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
};
}();
The module is then instantiated further down the line:
然后该模块被进一步实例化:
mobileNoHoverState.init();
回答by flintinatux
I really wanted a pure css
solution to this myself, since sprinkling a weighty javascript solution around all of my views seemed like an unpleasant option. Finally found the @media.hoverquery, which can detect "whether the primary input mechanism allows the user to hover over elements." This avoids touch devices where "hovering" is more of an emulated action than a direct capability of the input device.
我自己真的想要一个纯粹的css
解决方案,因为在我的所有观点周围撒上一个沉重的 javascript 解决方案似乎是一个令人不快的选择。终于找到了@media.hover查询,它可以检测“主要输入机制是否允许用户将鼠标悬停在元素上”。这避免了“悬停”更多是模拟动作而不是输入设备的直接功能的触摸设备。
So for example, if I have a link:
例如,如果我有一个链接:
<a href="/" class="link">Home</a>
Then I can safely style it to only :hover
when the device easily supports it with this css
:
然后我可以安全地将其样式设置为仅:hover
当设备轻松支持它时css
:
@media (hover: hover) {
.link:hover { /* hover styles */ }
}
While most modern browsers support interaction media feature queries, some popular browsers such as IE and Firefoxdo not. In my case this works fine, since I only intended to support Chrome on desktop and Chrome and Safari on mobile.
虽然大多数现代浏览器都支持交互媒体功能查询,但一些流行的浏览器(如 IE 和 Firefox)不支持。就我而言,这很好用,因为我只打算在桌面上支持 Chrome,在移动设备上支持 Chrome 和 Safari。
回答by Bnaya
My solution is to add hover-active css class to the HTML tag, and use it on the beginning of all the CSS selectors with :hover and remove that class on the first touchstart event.
我的解决方案是将悬停活动 css 类添加到 HTML 标记,并在所有 CSS 选择器的开头使用 :hover 并在第一个 touchstart 事件中删除该类。
http://codepen.io/Bnaya/pen/EoJlb
http://codepen.io/Bnaya/pen/EoJlb
JS:
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:
<html class="hover-active">
CSS:
CSS:
.hover-active .mybutton:hover {
box-shadow: 1px 1px 1px #000;
}
回答by Daft
Right, I jst had a similar problem but managed to fix it with media queries and simple CSS. I'm sure I'm breaking some rules here, but it's working for me.
是的,我刚刚遇到了类似的问题,但设法通过媒体查询和简单的 CSS 修复了它。我确定我在这里违反了一些规则,但它对我有用。
I basically had to take a massive application someone made, and make it responsive. They used jQueryUI and asked me not to tamper with any of their jQuery, so I was restricted to using CSS alone.
我基本上不得不采用某人制作的大型应用程序,并使其具有响应性。他们使用 jQueryUI 并要求我不要篡改他们的任何 jQuery,所以我只能单独使用 CSS。
When I pressed one of their buttons in touchscreen mode, the hover effect woudld fire for a second before the button's action took effect. Here's how I fixed it.
当我在触摸屏模式下按下他们的一个按钮时,悬停效果会在按钮的动作生效之前触发一秒钟。这是我修复它的方法。
@media only screen and (max-width:1024px) {
#buttonOne{
height: 44px;
}
#buttonOne:hover{
display:none;
}
}
回答by Stein G. Strindhaug
What I've done to solve the same problem is to have a feature detection (I use something like this code), seeing if onTouchMove is defined, and if so I add the css class "touchMode" to the body, else i add "desktopMode".
我为解决同样的问题所做的工作是进行特征检测(我使用类似此代码的内容),查看是否定义了 onTouchMove,如果定义了,我将 css 类“touchMode”添加到正文,否则我添加“桌面模式”。
Then every time some style effect only applies to a touch device, or only to a desktop the css rule is prepended with the appropriate class:
然后,每次某些样式效果仅适用于触摸设备或仅适用于桌面时,css 规则都会添加适当的类:
.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }
Edit: This strategy of course adds a few extra characters to your css, so If you're concerned about css size, you could search for the touchMode and desktopMode definitons and put them into different files, so you can serve optimized css for each device type; or you could change the class names to something much shorter before going to prod.
编辑:这个策略当然会为你的 css 添加一些额外的字符,所以如果你担心 css 大小,你可以搜索 touchMode 和 desktopMode 定义并将它们放在不同的文件中,这样你就可以为每个设备提供优化的 css类型; 或者您可以在开始之前将类名更改为更短的名称。
回答by Stanislav Berkov
In my project we solved this issue using https://www.npmjs.com/package/postcss-hover-prefixand https://modernizr.com/First we post-process output css files with postcss-hover-prefix
. It adds .no-touch
for all css hover
rules.
在我的项目中,我们使用https://www.npmjs.com/package/postcss-hover-prefix和https://modernizr.com/解决了这个问题,
首先我们使用postcss-hover-prefix
. 它添加.no-touch
了所有 csshover
规则。
const fs = require("fs");
const postcss = require("postcss");
const hoverPrfx = require("postcss-hover-prefix");
var css = fs.readFileSync(cssFileName, "utf8").toString();
postcss()
.use(hoverPrfx("no-touch"))
.process(css)
.then((result) => {
fs.writeFileSync(cssFileName, result);
});
css
css
a.text-primary:hover {
color: #62686d;
}
becomes
变成
.no-touch a.text-primary:hover {
color: #62686d;
}
At runtime Modernizr
automatically adds css classes to html
tag like this
在运行时Modernizr
自动添加 css 类到这样的html
标签
<html class="wpfe-full-height js flexbox flexboxlegacy canvas canvastext webgl
no-touch
geolocation postmessage websqldatabase indexeddb hashchange
history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage
borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients
cssreflections csstransforms csstransforms3d csstransitions fontface
generatedcontent video audio localstorage sessionstorage webworkers
applicationcache svg inlinesvg smil svgclippaths websocketsbinary">
Such post-processing of css plus Modernizr disables hover for touch devices and enables for others. In fact this approach was inspired by Bootstrap 4, how they solve the same issue: https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky-hoverfocus-on-mobile
css 和 Modernizr 的这种后处理会禁用触摸设备的悬停并启用其他设备。事实上,这种方法的灵感来自 Bootstrap 4,他们如何解决同样的问题:https: //v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky-hoverfocus-on-mobile
回答by Simon Dragsb?k
Iv'd found 2 solutions to the problem, which its implied that you detect touch with modernizr or something else and set a touch class on the html element.
我找到了该问题的 2 个解决方案,这意味着您使用 Modernizr 或其他方式检测触摸并在 html 元素上设置触摸类。
This is good but not supportedvery well:
html.touch *:hover {
all:unset!important;
}
But this has a very good support:
但这有很好的支持:
html.touch *:hover {
pointer-events: none !important;
}
Works flawless for me, it makes all the hover effects be like when you have a touch on a button it will light up but not end up buggy as the initial hover effect for mouse events.
对我来说完美无缺,它使所有悬停效果就像当您触摸按钮时它会亮起但最终不会像鼠标事件的初始悬停效果那样出现问题。
Detecting touch from no-touch devices i think modernizr has done the best job:
从非触摸设备检测触摸我认为 Modernizr 做得最好:
https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
EDIT
编辑
I found a better and simpler solution to this issue
我找到了一个更好更简单的解决方案来解决这个问题
回答by Said Kholov
You can trigger the mouseLeave
event whenever you touch an element on touchscreen. Here is a solution for all <a>
tags:
mouseLeave
每当您触摸触摸屏上的元素时,您都可以触发该事件。这是所有<a>
标签的解决方案:
function removeHover() {
var anchors = document.getElementsByTagName('a');
for(i=0; i<anchors.length; i++) {
anchors[i].addEventListener('touchstart', function(e){
$('a').mouseleave();
}, false);
}
}