如何防止触摸设备上的button粘滞hover效果

我创build了一个旋转木马,上一个和下一个button始终可见。 这些button有一个hover状态,他们变成蓝色。 在触摸设备上,如iPad,hover状态是粘滞的,所以点击后button保持蓝色。 我不要那个。

  • 我可以为每个button添加一个no-hover类,让我的CSS像这样: button:not(.no-hover):hover { background-color: blue; } button:not(.no-hover):hover { background-color: blue; }但是这可能对性能非常不利,并且无法正确处理像Chromebook Pixel(同时包含触摸屏和鼠标)的设备。

  • 我可以添加一个touch类到documentElement并使我的CSS如下所示: html:not(.touch) button:hover { background-color: blue; } html:not(.touch) button:hover { background-color: blue; }但是,这也不适用于具有触摸和鼠标的设备。

我更喜欢去掉hover状态ontouchend 。 但似乎并不是这样。 聚焦另一个元素不会消除hover状态。 手动点击另一个元素,但我似乎无法触发在JavaScript中。

我find的所有解决scheme似乎都不完善。 有没有完美的解决scheme?

您可以通过暂时删除DOM中的链接来删除hover状态。 请参阅http://testbug.handcraft.com/ipad.html


在CSS中你有:

 :hover {background:red;} 

在JS中你有:

 function fix() { var el = this; var par = el.parentNode; var next = el.nextSibling; par.removeChild(el); setTimeout(function() {par.insertBefore(el, next);}, 0) } 

然后在你的HTML你有:

 <a href="#" ontouchend="this.onclick=fix">test</a> 

一旦实现了CSS Media Queries Level 4 ,你就可以做到这一点:

 @media (hover: hover) { button:hover { background-color: blue; } } 

或者用英文表示:“如果浏览器支持正确/真实/真实/非仿真hover(例如,有一个类似于鼠标的主input设备),则在button shover时应用此样式。

由于第4层媒体查询的这一部分到目前为止只是在stream血的Chrome中实现, 所以我写了一个polyfill来处理这个问题。 使用它,你可以把上面的未来派的CSS变成:

 html.my-true-hover button:hover { background-color: blue; } 

.no-touch技术的一个变种)然后使用来自相同的polyfill的一些客户端JavaScript来检测对hover的支持,可以相应地切换my-true-hover类的存在:

 $(document).on('mq4hsChange', function (e) { $(document.documentElement).toggleClass('my-true-hover', e.trueHover); }); 

这是没有完美解决scheme的常见问题。 hover行为对于鼠标来说是有用的,并且大部分是有害的。 更复杂的问题是支持触摸和鼠标的设备(同时,不会less于!),就像Chromebook Pixel和Surface一样。

我发现最干净的解决scheme是只启用hover行为,如果设备不被视为支持触摸input。

 var isTouch = !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0; if( !isTouch ){ // add class which defines hover behavior } 

当然,你会失去hover在可能支持它的设备上。 然而,有时hover的影响比链接本身更多,例如,也许你想在一个元素被徘徊的时候显示一个菜单。 这种方法允许您testing触摸的存在,也许有条件地附加一个不同的事件。

我已经在iPhone,iPad,Chromebook Pixel,Surface以及各种Android设备上进行了testing。 当通用的USB触摸input(如触控笔)被添加到混音中时,我不能保证它可以工作。

使用Modernizr,您可以专门针对非触摸设备定位您的翱翔:

(注意:这不会在StackOverflow的片段系统上运行,请检查jsfiddle )

 /* this one is sticky */ #regular:hover, #regular:active { opacity: 0.5; } /* this one isn't */ html.no-touch #no-touch:hover, #no-touch:active { opacity: 0.5; } 

从4种方式来处理移动粘滞hover :这是一种基于用户的当前inputtypesdynamic添加或删除“ can touch ”类到文档的方法。 它也适用于混合设备,用户可以在触摸和鼠标/触控板之间切换:

 <script> ;(function(){ var isTouch = false //var to indicate current input type (is touch versus no touch) var isTouchTimer var curRootClass = '' //var indicating current document root class ("can-touch" or "") function addtouchclass(e){ clearTimeout(isTouchTimer) isTouch = true if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present curRootClass = 'can-touch' document.documentElement.classList.add(curRootClass) } isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event } function removetouchclass(e){ if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present isTouch = false curRootClass = '' document.documentElement.classList.remove('can-touch') } } document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad })(); </script> 
 $("#elementwithhover").click(function() { // code that makes element or parent slide or otherwise move out from under mouse. $(this).clone(true).insertAfter($(this)); $(this).remove(); }); 

我打算发布自己的解决scheme,但是如果有人已经发布了它,我发现@罗德尼差不多就这么做了。 不过,他错过了最后一个至关重要的因素,至less在我看来是无足轻重的。 我的意思是,我也采取了相同的.fakeHover类加法/删除通过.fakeHovermouseleave事件检测,但单独, 本身 ,行为几乎完全一样的“真正的” :hover 。 我的意思是:当你点击你桌子上的一个元素时,它不会检测到你已经“离开”它 – 从而保持“退色”状态。

我所做的只是简单地听click ,所以当我“点击”button时,我手动发射一个mouseleave

这是我的最终代码:

 .fakeHover { background-color: blue; } 

 $(document).on('mouseenter', 'button.myButton',function(){ $(this).addClass('fakeHover'); }); $(document).on('mouseleave', 'button.myButton',function(){ $(this).removeClass('fakeHover'); }); $(document).on('button.myButton, 'click', function(){ $(this).mouseleave(); }); 

这样,当您只是简单地将鼠标hover在button上时,就可以保持惯用的hoverfunction。 好吧,几乎所有的东西:唯一的缺点是,用鼠标点击button后,它不会处于hover状态。 就像如果你点击并迅速把指针从button之外。 但在我的情况下,我可以忍受。

添加这个JS代码到你的页面:

 document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover'; 

现在在每个hover之前在你的CSS添加像这样的hover类:

 .hover .foo:hover {} 

如果设备触摸,主体类将是空的,否则它的类将hover和规则应用!

我可以为每个button添加一个无hover的类,让我的CSS像这样:button:not(.no-hover):hover {background-color:blue; }但是这可能对性能非常不利,并且不能像Chromebook Pixel(同时包含触摸屏和鼠标)>正确地处理设备。

这是正确的出发点。 下一步:在下列事件上应用/删除nohover类(使用jQuery进行演示)

 buttonelement .on("touchend touchcancel",function(){$(this).addClass("nohover")}) .on("touchstart mouseover",function({$(this).removeClass("nohover")}); 

注意:如果您希望将其他类应用于buttonelement,CSS中的not(.nohover)将不再按预期工作。 比你必须添加一个单独的定义与默认值和!重要标签覆盖hover样式:.nohover {background-color:white!important}

这甚至应该正确处理像Chromebook Pixel(同时具有触摸屏和鼠标)的设备! 我不认为,这是一个主要的性能杀手…

这是我研究了其余的答案之后到目前为止所提出的。 它应该能够支持只触摸,仅鼠标或混合用户。

为hover效果创build单独的hover类。 默认情况下,将此hover类添加到我们的button。

我们不想检测触摸支持的存在,并从一开始就禁用所有的hover效果。 正如其他人所说,混合设备越来越受欢迎; 人们可能有触摸支持,但想要使用鼠标,反之亦然。 因此,只有在用户实际触摸button时才能移除hover类。

接下来的问题是,如果用户在触摸button后想要回到使用鼠标呢? 为了解决这个问题,我们需要find一个合适的时机来添加我们删除的hover类。

但是,由于hover状态仍处于活动状态,因此我们无法在删除后立即将其添加回去。 我们可能不想销毁并重新创build整个button。

所以,我想使用忙等待algorithm(使用setInterval)来检查hover状态。 一旦hover状态被停用,我们可以添加hover类并停止忙等待,使我们回到用户可以使用鼠标或触摸的原始状态。

我知道忙等待不是很好,但我不确定是否有适当的事件。 我已经考虑将它添加回mouseleave事件,但它不是很健壮。 例如,当触摸button后popup警报,鼠标位置移动,但不触发mouseleave事件。

 var button = document.getElementById('myButton'); button.ontouchstart = function(e) { console.log('ontouchstart'); $('.button').removeClass('button-hover'); startIntervalToResetHover(); }; button.onclick = function(e) { console.log('onclick'); } var intervalId; function startIntervalToResetHover() { // Clear the previous one, if any. if (intervalId) { clearInterval(intervalId); } intervalId = setInterval(function() { // Stop if the hover class already exists. if ($('.button').hasClass('button-hover')) { clearInterval(intervalId); intervalId = null; return; } // Checking of hover state from // http://stackoverflow.com/a/8981521/2669960. var isHovered = !!$('.button').filter(function() { return $(this).is(":hover"); }).length; if (isHovered) { console.log('Hover state is active'); } else { console.log('Hover state is inactive'); $('.button').addClass('button-hover'); console.log('Added back the button-hover class'); clearInterval(intervalId); intervalId = null; } }, 1000); } 
 .button { color: green; border: none; } .button-hover:hover { background: yellow; border: none; } .button:active { border: none; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button id='myButton' class='button button-hover'>Hello</button> 

你可以设置背景颜色:active状态,并给:focus的背景。

如果通过onfocus/ontouch设置背景颜色,颜色样式将保持一次:focus状态已经消失。
你需要在onblur重置,以便在重点丢失时恢复默认的bg。

这对我有帮助: 链接

 function hoverTouchUnstick() { // Check if the device supports touch events if('ontouchstart' in document.documentElement) { // Loop through each stylesheet for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) { var sheet = document.styleSheets[sheetI]; // Verify if cssRules exists in sheet if(sheet.cssRules) { // Loop through each rule in sheet for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) { var rule = sheet.cssRules[ruleI]; // Verify rule has selector text if(rule.selectorText) { // Replace hover psuedo-class with active psuedo-class rule.selectorText = rule.selectorText.replace(":hover", ":active"); } } } } } } 

这为我工作:把hover式样在一个新的类

 .fakehover {background: red} 

然后根据需要添加/删除类

 $(".someclass > li").on("mouseenter", function(e) { $(this).addClass("fakehover"); }); $(".someclass > li").on("mouseleave", function(e) { $(this).removeClass("fakehover"); }); 

重复touchstart和touchend事件。 或者任何喜欢得到想要的结果的事件,例如我想让hover效果在触摸屏上切换。

一个为我工作的解决scheme:

 html { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } 

将此代码添加到您的样式表。

我想摆脱点击链接时出现在iOS Safari上的灰色背景。 但似乎做得更多。 现在点击一个button(带有:hover伪类!)马上被打开! 我只在iPad上testing过,我不知道它是否能在其他设备上工作。

我想我已经find了一个类似问题的优雅(最小的JS)解决scheme:

使用jQuery,你可以使用.mouseover()来触发在body(或任何其他元素)上的hover,

所以我简单地把这个处理程序附加到元素的ontouchend事件上,如下所示:

 var unhover = function() { $("body").mousover(); }; 
 .hoverable { width: 100px; height: 100px; background: teal; cursor: pointer; } .hoverable:hover { background: pink; } 
 <div class="hoverable" ontouchend={unhover}></div> 

根据Darren Cooks的回答,如果你将手指放在另一个元素上,这也是有效的。

在触发事件期间,请参阅查找元素手指处于打开状态

 jQuery(function() { FastClick.attach(document.body); }); // Prevent sticky hover effects for buttons on touch devices // From https://stackoverflow.com/a/17234319 // // // Usage: // <a href="..." touch-focus-fix>..</a> // // Refactored from a directive for better performance and compability jQuery(document.documentElement).on('touchend', function(event) { 'use strict'; function fix(sourceElement) { var el = $(sourceElement).closest('[touch-focus-fix]')[0]; if (!el) { return; } var par = el.parentNode; var next = el.nextSibling; par.removeChild(el); par.insertBefore(el, next); } fix(event.target); var changedTouch = event.originalEvent.changedTouches[0]; // http://www.w3.org/TR/2011/WD-touch-events-20110505/#the-touchend-event if (!changedTouch) { return; } var touchTarget = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY); if (touchTarget && touchTarget !== event.target) { fix(touchTarget); } }); 

Codepen演示

你可以试试这个方法

JavaScript的:

 var isEventSupported = function (eventName, elementName) { var el = elementName ? document.createElement(elementName) : window; eventName = 'on' + eventName; var isSupported = (eventName in el); if (!isSupported && el.setAttribute) { el.setAttribute(eventName, 'return;'); isSupported = typeof el[eventName] == 'function'; } el = null; return isSupported; }; if (!isEventSupported('touchstart')) { $('a').addClass('with-hover'); } 

CSS:

 a.with-hover:hover { color: #fafafa; } 

我到目前为止在我的项目中做的是恢复:hover触摸设备上的:hover更改:

 .myhoveredclass { background-color:green; } .myhoveredclass:hover { background-color:red; } @media screen and (-webkit-min-device-pixel-ratio:0) { .myhoveredclass:hover, .myhoveredclass:active, .myhoveredclass:focus { background-color:green; } } 

所有类名称和命名颜色仅用于演示目的;-)

这完美分两步完成。

  1. 设置你的身体标签是这样<body ontouchstart=""> 。 我不是这种“黑客”的粉丝,但它允许iOS上的Safari即时响应触摸。 不知道如何,但它的作品。

  2. 设置你的可触摸类如下:

     // I did this in SASS, but this should work with normal CSS as well // Touchable class .example { // Default styles background: green; // Default hover styles // (Think of this as Desktop and larger) &:hover { background: yellow; } // Default active styles &:active { background: red; } // Setup breakpoint for smaller device widths @media only screen and (max-width: 1048px) { // Important! // Reset touchable hover styles // You may want to use the same exact styles as the Default styles &:hover { background: green; } // Important! // Touchable active styles &:active { background: red; } } } 

您也可以删除触摸式课程中的所有animation。 Android Chrome似乎比iOS慢一点。

如果用户在触摸课程的同时滚动页面,这也会导致应用活动状态。