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

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

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

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

如果有人想提出建议,我不想使用用户代理嗅探。同样的设备可以同时拥有触摸屏和鼠标(也许现在不那么普遍,但将来会更加普遍)。我对这个设备不感兴趣,我感兴趣的是它目前的使用情况: 鼠标还是触摸屏。

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复制,但这里有一个快速复制的参考。

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

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

我还发布了一个实现上述功能的样例 给你,它还可以挂钩 jQuery 的鼠标事件。您可以使用它来查看点击事件也将触发 mouseentermousemovemouseleave

116101 次浏览

它可能有助于查看您的 CSS,因为它听起来是一个相当奇怪的问题。但是无论如何,如果它正在发生,并且其他一切正常,您可以尝试将悬停效果转换为 javascript (您也可以使用 jquery)。 简单地说,绑定到 mouseover 事件或者更好的做法是,在事件触发时点亮您的元素。

检查这里的最后一个例子: http://api.jquery.com/mouseover/,您可以使用类似于日志的东西时,事件触发,并从那里!

How can I get the hover effects when I'm using the mouse, but suppress them when I'm using the touchscreen?

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

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

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

因此,即使“屏幕”或“手持”部分被忽略的“最大宽度”将为您做的技巧。你可以假设任何屏幕小于800像素的东西都是平板电脑或手机,而不使用悬停效果。对于那些在低分辨率设备上使用鼠标的少数用户来说,他们不会看到悬停效果,但是如果不是这样的话,你的站点就不会有问题。

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

如果你将鼠标悬停效果从 CSS 中移除,并使用 JavaScript 应用它们,那么你就可以专门绑定到鼠标事件,并且/或者你可以仅仅根据屏幕大小做一些假设,最糟糕的情况“问题”是,一些使用鼠标的用户错过了悬停效果。

为了解决同样的问题,我做了一个特征提取(我使用类似于 这个密码的东西) ,看看 onTouchMove 是否定义了,如果是这样,我将 css 类“ touch Mode”添加到主体中,否则我将添加“ desk topMode”。

然后,每当一些样式效果只适用于触摸设备,或者只适用于桌面时,css 规则就会被添加到相应的类中:

.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }

编辑 : 这个策略当然会给你的 css 增加一些额外的字符,所以如果你关心 css 的大小,你可以搜索 touch Mode 和 desk Mode 的定义并把它们放到不同的文件中,这样你就可以为每种设备类型提供优化的 css; 或者你可以在点击之前把类名改成更短的。

如果你喜欢使用 JavaScript,那么你可以在你的页面中使用 现代化。当页面加载时,一个非触摸屏浏览器将有类’。No-touch’添加到 html 标签中,但是对于触摸屏浏览器,html 标签将有类’。添加到 html 标签。

然后,在决定添加 mouseenter 和 mouseleave 侦听器之前,只需检查 html 标记是否具有 no-touch 类即可。

if($('html').hasClass('no-touch')){
$('.box').on("mouseenter", function(event){
$(this).css('background-color','#0000ff')
});
$('.box').on("mouseleave", function(event){
$(this).css('background-color','')
});
}

对于触摸屏设备的事件将没有监听器,所以你将不会得到悬停效果,当你点击。

在我最近做的一个项目中,我用 jQuery 的委托事件特性解决了这个问题。它使用 jQuery 选择器查找某些元素,并在鼠标位于元素上方时向这些元素添加/删除一个 CSS 类。它似乎工作良好,因为我已经能够测试它,其中包括 IE10在一个触摸式笔记本电脑上运行 Windows 8。

$(document).ready(
function()
{
// insert your own selector here: maybe '.hoverable'?
var selector = 'button, .hotspot';


$('body')
.on('mouseover', selector, function(){ $(this).addClass('mouseover');    })
.on('mouseout',  selector, function(){ $(this).removeClass('mouseover'); })
.on('click',     selector, function(){ $(this).removeClass('mouseover'); });
}
);

编辑: 当然,这个解决方案需要您修改 CSS 以删除“ : hover”选择器,并提前考虑您希望“ hover”的元素。

如果您的页面上有非常多的元素(比如几千个) ,那么它可能会有点慢,因为这个解决方案在页面中捕获 all elements上的三种类型的事件,然后在选择器匹配的情况下执行它的任务。我将 CSS 类命名为“ mouseover”而不是“ hover”,因为我不希望任何 CSS 读者读取“ : hover”。盘旋”。

是的,我也遇到过类似的问题,但是通过媒体查询和简单的 CSS 就解决了。我知道我违反了一些规定,但这对我很管用。

我基本上就是拿了别人做的一个大型应用程序,然后让它响应。他们使用 jQueryUI,并要求我不要篡改他们的任何 jQuery,所以我只能单独使用 CSS。

当我在触摸屏模式下按下他们的一个按钮时,悬停效果会持续一秒钟,然后按钮的操作才会生效。我是这么修的。

@media only screen and (max-width:1024px) {


#buttonOne{
height: 44px;
}




#buttonOne:hover{
display:none;
}
}

我为最近的一个项目写了下面的 JS,这是一个桌面/移动/平板电脑网站,有悬浮效果,不应该出现在触摸屏上。

下面的 mobileNoHoverState模块有一个变量 preventMouseover(最初声明为 false) ,当用户对元素 $target触发 touchstart事件时,该变量被设置为 true

然后,当触发 mouseover事件时,将 preventMouseover设置回 false,这样,如果用户同时使用触摸屏和鼠标,站点就可以按预期的方式工作。

我们知道 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
};
}();

然后进一步实例化该模块:

mobileNoHoverState.init();

根据你的问题,我认为悬停效应会改变页面的内容。在这种情况下,我的建议是:

  • Add hover effects on touchstart and mouseenter.
  • 消除对 mouseleavetouchmoveclick的悬停效果。

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

背景资料

为了模拟鼠标,像 Webkit 这样的浏览器会在用户触摸触摸屏时触发以下事件(比如 iPad)(来源: html5rocs.com 上的 触摸与鼠标) :

  1. touchstart
  2. touchmove
  3. touchend
  4. 300毫秒的延迟,浏览器确保这是一个单击,而不是双击
  5. mouseover
    • Note: If a mouseover, mouseenter or mousemove event changes the page content, the following events are never fired.
  6. mousemove
  7. mousedown
  8. mouseup
  9. click

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

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

解答

总结一下:

  • touchstartmouseenter上添加悬停效果。
  • 消除对 mouseleavetouchmoveclick的悬停效果。

请注意,在 touchend上没有操作!

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

如果用户实际上使用 clicks 链接,那么悬停效果也会被移除。这样可以确保当用户在浏览器中按下后退按钮时将其删除。

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毫秒延迟实际上得到了很好的利用,因为悬停效果将在这短时间内显示出来。

如果用户决定取消点击,手指的一个移动将这样做正常。通常,这是一个问题,因为没有触发 mouseleave事件,并且悬停效应仍然存在。谢天谢地,这可以很容易地通过消除悬停效应的 touchmove

就是这样!

注意,可以消除300ms 的延迟,例如使用 快速点击库,但这超出了这个问题的范围。

替代解决方案

我发现以下几种选择存在以下问题:

  • 浏览器检测: 极易出错。假设一个设备有鼠标或者触摸,当触摸显示越来越多的时候,两者的结合将会变得越来越普遍。
  • CSS 媒体检测: 我所知道的唯一只有 CSS 的解决方案。仍然容易出错,并且仍然假设一个设备有鼠标或触摸,虽然两者都有可能。
  • 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.
  • 使用一个变量来抑制鼠标事件: 这在 touchend中设置一个变量,在后续的鼠标事件中作为 if 条件使用,以防止该时间点的状态更改。变量在 click 事件中重置。请看这一页 Walter Roman 的回答。如果你真的不想在触摸界面上出现悬停效果,这是一个不错的解决方案。不幸的是,如果由于其他原因触发了 touchend,而且没有触发点击事件(例如用户滚动或放大) ,并且随后试图用鼠标跟踪链接(例如在同时具有鼠标和触摸界面的设备上) ,那么这种方法就不起作用。

进一步阅读

以下是我的解决方案: http://jsfiddle.net/agamemnus/g56aw709/--代码如下。

所有人需要做的就是将他们的“ : 盘旋”转换为“。”。“盘旋”... 就是这样!它与其他选择器的最大区别在于,它也适用于非奇异元素选择器,比如 .my_class > *:hover {

handle_css_hover_effects ()


function handle_css_hover_effects (init) {
var init = init || {}
var handle_touch_events = init.handle_touch_events || true
var handle_mouse_events = init.handle_mouse_events || true
var hover_class         = init.hover_class         || "hover"
var delay_preferences   = init.delay_preferences   || {touch: {add: 500, remove: 500}}
function default_handler (curobj, input_type, op) {
var hovered_element_selector = "*" + ((op == "add") ? ":" : ("." + hover_class))
var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll(hovered_element_selector))
var modified_list = []
while (true) {
if ((curobj == null) || (curobj == document.documentElement)) break
if (hovered_elements.indexOf(curobj) != -1) modified_list.push (curobj)
curobj = curobj.parentNode
}
function do_hover_change () {modified_list.forEach (function (curobj) {curobj.classList[op](hover_class)})}
if ((!delay_preferences[input_type]) || (!delay_preferences[input_type][op])) {
do_hover_change ()
} else {
setTimeout (do_hover_change, delay_preferences[input_type][op])
}
}


if (handle_mouse_events) {
document.body.addEventListener ('mouseover' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "add")})
document.body.addEventListener ('mouseout'  , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
document.body.addEventListener ('click'     , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
}


if (handle_touch_events) {
document.body.addEventListener ('touchstart', function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "add")})
document.body.addEventListener ('touchend'  , function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "remove")})
document.body.addEventListener ('touchmove',  function (evt) {
var curobj = evt.target
var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll("*:hover"))
var lastobj = null
evt = evt.changedTouches[0]
var elements_at_point = get_elements_at_point (evt.pageX, evt.pageY)
// Get the last element that isn't at the current point but is still hovered over, and remove only its hover attribute.
while (true) {
if ((curobj == null) || (curobj == document.documentElement)) break
if ((hovered_elements.indexOf(curobj) != -1) && (elements_at_point.indexOf(curobj) == -1)) lastobj = curobj
curobj = curobj.parentNode
}
if (lastobj == null) return
if ((!delay_preferences.touch) || (!delay_preferences.touch.remove)) {
lastobj.classList.remove(hover_class)
} else {
setTimeout (function () {lastobj.classList.remove(hover_class)}, delay_preferences.touch.remove)
}


function get_elements_at_point (x, y) {
var el_list = [], pe_list = []
while (true) {
var curobj = document.elementFromPoint(x, y)
if ((curobj == null) || (curobj == document.documentElement)) break
el_list.push (curobj); pe_list.push (curobj.style.pointerEvents)
curobj.style.pointerEvents = "none"
}
el_list.forEach (function (current_element, i) {current_element.style.pointerEvents = pe_list[i]})
return el_list
}
})
}
}

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

Http://codepen.io/bnaya/pen/eojlb

约翰逊:

(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;
}

我已经找到了解决这个问题的两个方案,这意味着您可以检测到与 Modern izr 或其他东西的接触,并在 html 元素上设置一个 touch 类。

这很好,但是 < a href = “ https://developer.mozilla.org/en-US/docs/Web/CSS/all”rel = “ nofollow noReferrer”> 支持 不是很好:

html.touch *:hover {
all:unset!important;
}

但是这有一个非常好的 支持:

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.

从非触摸设备中检测触摸,我认为 Modern izr 做得最好:

Https://github.com/modernizr/modernizr/blob/master/feature-detects/touchevents.js

EDIT

我找到了一个更好更简单的解决方案

如何确定客户端是否为触摸设备

只要触摸触摸屏上的元素,就可以触发 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);
}
}

在你的页面上包含 现代化,然后像这样设置悬停状态:

html.no-touchevents .box:hover {
background: blue;
}

我真的想要一个纯粹的 css解决方案来解决这个问题,因为在我的所有视图周围添加一个沉重的 javascript 解决方案似乎是一个不愉快的选择。最后找到了 @ media. hover查询,它可以检测“主输入机制是否允许用户将鼠标悬停在元素上方”这样可以避免触摸设备,因为“悬停”更像是一个模拟动作,而不是输入设备的直接功能。

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

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

Then I can safely style it to only :hover when the device easily supports it with this css:

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

虽然大多数现代浏览器支持交互媒体特性查询,但一些流行的浏览器 such as IE and Firefox却不支持。在我的案例中,这个工作很好,因为我只打算在桌面上支持 Chrome,在移动设备上支持 Chrome 和 Safari。

你好,来自未来的人,你可能想使用 pointer和/或 hover媒体查询。不推荐使用 handheld媒体查询。

/* device is using a mouse or similar */
@media (pointer: fine) {
a:hover {
background: red;
}
}

在我的项目中,我们使用 https://www.npmjs.com/package/postcss-hover-prefix和 < a href = “ https://Modern izr.com/”rel = “ nofollow norefrer”> https://modernizr.com/解决了这个问题 首先我们用 postcss-hover-prefix对 css 输出文件进行后处理。它为所有 css hover规则添加了 .no-touch

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

a.text-primary:hover {
color: #62686d;
}

变成了

.no-touch a.text-primary:hover {
color: #62686d;
}

在运行时,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">

这样的后期处理的 css 加上现代化禁用盘旋触摸设备,并为其他人启用。事实上,这种方法的灵感来自 Bootstrap 4,它们如何解决同样的问题: https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky-hoverfocus-on-mobile

尝试这个简单的2019 jquery 解决方案,虽然它已经出现了一段时间;

  1. 将这个插件添加到磁头:

    src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. 把这个添加到 js:

    $("*").on("touchend", function(e) { $(this).focus(); }); //applies to all elements

  3. 这方面的一些建议变体是:

    $(":input, :checkbox,").on("touchend", function(e) {(this).focus);}); //specify elements
    
    
    $("*").on("click, touchend", function(e) { $(this).focus(); });  //include click event`
    
    
    css: body { cursor: pointer; } //touch anywhere to end a focus`
    

Notes

  • place plugin before bootstrap.js, if applicable, to avoid affecting tooltips
  • only tested on iphone XR ios 12.1.12, and ipad 3 ios 9.3.5, using Safari or Chrome.

References:

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/

.services-list .fa {
transition: 0.5s;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
color: blue;
}
/* For me, @media query is the easiest way for disabling hover on mobile devices */
@media only screen and (min-width: 981px) {
.services-list .fa:hover {
color: #faa152;
transition: 0.5s;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
/* You can actiate hover on mobile with :active */
.services-list .fa:active {
color: #faa152;
transition: 0.5s;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
.services-list .fa-car {
font-size:20px;
margin-right:15px;
}
.services-list .fa-user {
font-size:48px;
margin-right:15px;
}
.services-list .fa-mobile {
font-size:60px;
}
<head>
<title>Hover effects on mobile browsers</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>


<body>
<div class="services-list">
<i class="fa fa-car"></i>
<i class="fa fa-user"></i>
<i class="fa fa-mobile"></i>
</div>
</body>

例如: https://jsfiddle.net/lesac4/jg9f4c5r/8/

您可以使用 js,它应该按预期工作。

function myFunction(){
var x = document.getElementById("DIV");
x.style.backgroundColor="red";
x.style.cursor="pointer";
x.style.color="white"
}
function my2Function(){
var x = document.getElementById("DIV");
x.style.backgroundColor="white";
x.style.color="red"
}
.mydiv {
background-color: white;
color: red;
}
<div class = "mydiv" id="DIV" onmouseover="myFunction()" onmouseleave="my2Function()">
hi
</div>

Here my issue has been fixed(mouseenter & touch related issue in React js) by using

onMouseEnter={() => addHeaderClassName()} onMouseLeave={() => removeHeaderClassName()} onFocus={() => addHeaderClassName()} onBlur={() => removeHeaderClassName()}

上面提到的“ onMouseEnter & onMouseleave”适用于大型设备,比如可以检测到鼠标事件的桌面,另一方面“ onFocus & onBlur”适用于小型设备,比如可以检测到触摸的平板电脑和移动设备。

在这里我的问题已经被修复(鼠标输入和触摸相关的问题在 React js)通过使用

onMouseEnter={() => addHeaderClassName()} onMouseLeave={() => removeHeaderClassName()} onFocus={() => addHeaderClassName()} onBlur={() => removeHeaderClassName()}

上面提到的“ onMouseEnter & onMouseleave”适用于大型设备,比如可以检测到鼠标事件的桌面,另一方面“ onFocus & onBlur”适用于小型设备,比如可以检测到触摸的平板电脑和移动设备。