是否可以对iPhone / iPad用户强制忽略:hover伪类?


94

我的网站上有一些CSS菜单,可扩展为:hover(没有js)

这在iDevices上以半破坏的方式起作用,例如,点击将激活:hover规则并展开菜单,但是随后在其他地方点击不会删除:hover。同样,如果元素中有一个链接,则:hover必须点击两次以激活该链接(第一次点击触发:hover,第二次点击触发链接)。

通过绑定touchstart事件,我已经能够使事情在iphone上正常运行。

问题在于,有时移动浏览器仍会选择:hover从css 而非我的touchstart事件中触发规则!

我知道这是问题所在,因为当我禁用所有 :hover在css中手动规则时,移动safari效果很好(但是常规浏览器显然不再可用了)。

有没有办法动态地“取消” :hover当用户使用移动Safari时,某些元素的规则?

在此处查看和比较iOS行为:http : //jsfiddle.net/74s35/3/ 注意:只有某些CSS属性会触发双击行为,例如display:none;。但不是背景:红色;或文字装饰:下划线;


7
请不要仅因为存在“触摸”事件而禁用悬停。这将对具有触摸和鼠标输入设备的笔记本电脑的用户产生不利影响。请参阅我的答案以了解更多详细信息
Simon_Weaver 2014年

Answers:


77

我发现在iPhone / iPad Safari中“:hover”是不可预测的。有时点击元素会使该元素“:hover”,而有时会漂移到其他元素。

暂时,我身上只有一个“无接触”课程。

<body class="yui3-skin-sam no-touch">
   ...
</body>

并且将所有CSS规则的“ .no-touch”下面带有“:hover”:

.no-touch my:hover{
   color: red;
}

在页面的某处,我有JavaScript可以从正文中删除非接触式类。

if ('ontouchstart' in document) {
    Y.one('body').removeClass('no-touch');
}

这看起来并不完美,但是仍然可以正常工作。


5
这是唯一的方法。令我困扰的是,它污染了“正常”网站,产生了不属于该网站且与之无关的内容。那好吧。谢谢。
Christopher Camps

您还可以使用带有IE备用广告
Lime,

2
@Shackrock:我认为“一键式悬停,两键单击”不是一个好的解决方案。由于实际上在触摸设备上没有实际的悬停(直到它们带有感应到您的手指悬停在其上的屏幕),所以我认为最好不要完全模拟悬停。对于效果,如按钮和链接上常见的效果,只需跳过效果。对于在悬停时扩展的菜单,请使其在点击/单击时扩展。我的2c。
阿德里安·施密特

18
我在这种方法中发现的问题最近由于新的Windows 8系统,配备触摸和鼠标输入
汤姆

4
汤姆(Tom)的评论+1-即使具备触摸功能的笔记本电脑(即使更需要带有悬停状态的鼠标友好站点)插入外接显示器,检查ontouchstart也会返回true。
gregdev13年

45

:hover这里不是问题。iOS的Safari遵循一个非常奇怪的规则。它激发mouseovermousemove第一; 如果在这些事件期间发生任何更改,则不会触发“点击”和相关事件:

iOS中的触摸事件图

mouseentermouseleave似乎包括在内,尽管未在图表中指定。

如果您由于这些事件而修改了任何内容,则不会触发点击事件。这包括DOM树中更高的内容。例如,这将防止单击使用jQuery在您的网站上工作:

$(window).on('mousemove', function() {
    $('body').attr('rel', Math.random());
});

编辑:为澄清起见,jQuery的hover事件包括mouseentermouseleave。这些都将阻止click内容的更改。


5
当然:hover是问题:-)或Safari的越野车实现。这是有关该问题的有趣背景,但苹果公司是唯一可以解决此可怕行为的公司。单击元素外部的内容时,它要么需要“悬停”,要么根本不需要“悬停”。当前的行为基本上破坏了任何使用:hover鼠标的网站
Simon_Weaver 2014年

这确实帮助我意识到,jQuery hover处理程序阻止了单独的click处理程序被击中- 真是个玩笑!
Simon_Weaver

1
出色的答案,以及对我遇到的问题的最佳解释。导致“双击”问题的不仅是CSS悬停系统,而是在mouseover / mouseenter等事件发生后,几乎所有的DOM修改。
奥利弗·约瑟夫·阿什

这是一个示例,显示了在mouseenter jsbin.com/maseti/5/edit?html,js,output
Oliver Joseph Ash

@Oliver Joseph Ash Right,因此是答案中的JS示例。;)
Zenexer

18

浏览器功能检测库Modernizer包含对触摸事件的检查。

默认行为是为每个要检测的功能将类应用于html元素。然后,您可以使用这些类来设置文档样式。

如果未启用触摸事件,Modernizr可以添加以下类no-touch

<html class="no-touch">

然后在这个类中定义您的悬停样式:

.no-touch a:hover { /* hover styles here */ }

您可以下载自定义的Modernizr构建,以根据需要包含尽可能少的特征检测。

这是一些可以应用的类的示例:

<html class="js no-touch postmessage history multiplebgs
             boxshadow opacity cssanimations csscolumns cssgradients
             csstransforms csstransitions fontface localstorage sessionstorage
             svg inlinesvg no-blobbuilder blob bloburls download formdata">

请注意,在Modernizr报告为“触摸”的台式机的最新版本的Chrome上。显然在版本3中使用touchevents已对此进行了纠正。
Craig Jacobs 2015年

18

一些设备(如其他人所说)具有触摸和鼠标事件。例如,Microsoft Surface具有触摸屏,触控板和手写笔,当将其悬停在屏幕上方时,实际上会引发悬停事件。

任何禁用的解决方案 :hover基于“触摸”事件的存在而的都将影响Surface用户(以及许多其他类似设备)。许多新的笔记本电脑都是触摸式的,并且会响应触摸事件-因此,禁用悬停是一种非常糟糕的做法。

这是Safari中的错误,这种可怕的行为绝对没有道理。我拒绝破坏非iOS浏览器,因为iOS Safari中的一个错误似乎已经存在多年了。我真的希望他们下周针对iOS8修复此问题,但与此同时...。

我的解决方案

有些人已经建议使用Modernizr,那么Modernizr允许您创建自己的测试。我在这里基本上在做的是“抽象”浏览器的想法,该想法支持:hoverModernizr测试,使我可以在整个代码中使用它而无需对if (iOS)整个代码进行硬编码。

 Modernizr.addTest('workinghover', function ()
 {
      // Safari doesn't 'announce' to the world that it behaves badly with :hover
      // so we have to check the userAgent  
      return navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? false : true;
 });

然后css变成这样的东西

html.workinghover .rollover:hover 
{
    // rollover css
}

仅在iOS上,此测试将失败并禁用翻转。

这种抽象的最好的部分是,如果我发现它在某个android上坏了,或者在iOS9中已修复,那么我可以修改测试。


这种解决方案既讨厌又是最好的解决方案。仅当在悬停时遇到困难的浏览器同时具有触摸和点击功能时,它才会中断,但对于ipad / iphone上的仅触摸式浏览器,情况并非如此。我的建议是仅将此实现作为优化修复(以消除已经使用fastclick.js时的悬停闪烁),并确保没有它也可以正常工作。
Gersom

谢谢。我同意。想知道iOS 9针对此问题做了什么-如果有的话。
Simon_Weaver

我走了相反的方向:html:not(.no-hover) .category:hover { ...
克里斯·马里西克

+1为“ Safari不会向世界宣布它在:hover上表现不佳”,这个问题使iOS比我认为的任何版本的IE都要糟糕...
Spencer O'Reilly

16

FastClick库添加到您的页面将导致移动设备上的所有点击都变为点击事件(无论用户单击何处),因此它还应解决移动设备上的悬停问题。我以您的小提琴为例进行了编辑:http : //jsfiddle.net/FvACN/8/

只需在页面上包含fastclick.min.js库,并通过以下方式激活:

FastClick.attach(document.body);

附带的好处是,它还将消除移动设备遭受的烦人的300ms onClick延迟。


使用FastClick对您的网站可能会或可能不会有几个小影响:

  1. 如果您在页面上的某个位置点击,向上滚动,向下滚动,然后将手指放到与最初放置位置完全相同的位置,FastClick会将其解释为“单击”,即使显然不是。至少这就是我当前使用的FastClick版本(1.0.0)的工作方式。自该版本以来,可能已经解决了该问题。
  2. FastClick消除了某人“双击”的功能。

1
您是否愿意分享另一个具体示例,说明如何为我的网站做到这一点。我对javascript一无所知,所以我对它的工作方式有些困惑。我的网站上通常有所有这些链接,用户通常在单击之前将鼠标悬停在这些链接上,但是对于ipad / mobile,我想这样做,以便他们不必单击两次。
安迪2015年

@Andy-尚不清楚您需要什么,缺少什么...到目前为止,您已经尝试了什么,看到了什么错误(如果有)?也许最好是创建一个新的Stackoverflow问题,如果还没有的话,请举一个您想做的事的例子(?),或者尝试在上面的jsfiddle例子中弄清楚您没有做的事。不明白。
特洛伊

可以在单页应用程序中使用吗?我的意思是每次都更新DOM内容时
Sebastien Lorber

是的,我正在单页应用程序中使用它。
Troy


4

基本上有三种情况:

  1. 用户只有鼠标/指针设备,可以激活:hover
  2. 用户只有触摸屏,无法激活:hover元素
  3. 用户既有触摸屏又有指针设备

最初接受的答案工程巨大的,如果只有前两种情况是可能的,其中一个用户无论是指针或触摸屏。4年前,当OP问这个问题时,这很普遍。一些用户指出Windows 8和Surface设备使第三种情况更有可能出现。

iOS解决方案无法将鼠标悬停在触摸屏设备上(由@Zenexer详细介绍)很聪明,但会导致直接的代码出现异常(如OP所述)。仅对触摸屏设备禁用悬停意味着您仍然需要编写对触摸屏友好的替代方法。检测到用户何时同时具有指针和触摸屏会进一步使水浑浊(如@Simon_Weaver所述)。

在这一点上,最安全的解决方案是避免将其:hover用作用户与您的网站进行交互的唯一方法。悬停效果是指示链接或按钮可操作的好方法,但是不要求用户悬停元素来在您的网站上执行操作。

重新考虑触摸屏的“悬停”功能对替代UX方法进行了很好的讨论。答案提供的解决方案包括:

  • 用直接操作替换悬停菜单(始终可见链接)
  • 用轻按菜单替换悬停菜单
  • 将大量悬停内容移动到单独的页面中

展望未来,这可能是所有新项目的最佳解决方案。可接受的答案可能是第二好的解决方案,但是请确保考虑到也具有指针设备的设备。当设备具有触摸屏时,请注意不要消除功能,以解决iOS的:hover攻击问题。


1

.css中的JQuery版本使用.no-touch .my-element:hover,因为所有的悬停规则都包括JQuery和以下脚本

function removeHoverState(){
    $("body").removeClass("no-touch");
}

然后在body标签中添加class =“ no-touch” ontouchstart =“ removeHoverState()”

一旦ontouchstart触发,所有悬停状态的类将被删除


0

我创建了一个用于处理触摸事件的系统,而不是仅在无法使用触摸时才具有悬停效果,从而为我解决了这个问题。首先,我定义了一个对象来测试“点击”(相当于“点击”)事件。

touchTester = 
{
    touchStarted: false
   ,moveLimit:    5
   ,moveCount:    null
   ,isSupported:  'ontouchend' in document

   ,isTap: function(event)
   {
      if (!this.isSupported) {
         return true;
      }

      switch (event.originalEvent.type) {
         case 'touchstart':
            this.touchStarted = true;
            this.moveCount    = 0;
            return false;
         case 'touchmove':
            this.moveCount++;
            this.touchStarted = (this.moveCount <= this.moveLimit);
            return false;
         case 'touchend':
            var isTap         = this.touchStarted;
            this.touchStarted = false;
            return isTap;
         default:
            return true;
      }
   }
};

然后,在事件处理程序中,我执行以下操作:

$('#nav').on('click touchstart touchmove touchend', 'ul > li > a'
            ,function handleClick(event) {
               if (!touchTester.isTap(event)) {
                  return true;
               }

               // touch was click or touch equivalent
               // nromal handling goes here.
            });

0

感谢@Morgan Cheng的回答,但是我稍微修改了JS函数以获取“ touchstart ”(代码来自@Timothy Perez answer),但是,您需要jQuery 1.7+

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });

0

根据Zenexer提供的响应,不需要其他HTML标签的模式是:

jQuery('a').on('mouseover', function(event) {
    event.preventDefault();
    // Show and hide your drop down nav or other elem
});
jQuery('a').on('click', function(event) {
    if (jQuery(event.target).children('.dropdown').is(':visible') {
        // Hide your dropdown nav here to unstick
    }
});

此方法首先触发鼠标悬停,然后单击第二次。


0

我同意禁用悬停触摸是一种方法。

但是,为了避免自己重写CSS的麻烦,只需将所有:hover项目包装在 @supports not (-webkit-overflow-scrolling: touch) {}

.hover, .hover-iOS {
  display:inline-block;
  font-family:arial;
  background:red;
  color:white;
  padding:5px;
}
.hover:hover {
  cursor:pointer;
  background:green;
}

.hover-iOS {
  background:grey;
}

@supports not (-webkit-overflow-scrolling: touch) {
  .hover-iOS:hover {
    cursor:pointer;
    background:blue;
  }

}
<input type="text" class="hover" placeholder="Hover over me" />

<input type="text" class="hover-iOS" placeholder="Hover over me (iOS)" />


0

对于具有:hover在iOS Safari上禁用事件的常见用例的用户,最简单的方法是对事件使用最小宽度媒体查询,:hover该查询保持在要避免的设备的屏幕宽度以上。例:

@media only screen and (min-width: 1024px) {
  .my-div:hover { // will only work on devices larger than iOS touch-enabled devices. Will still work on touch-enabled PCs etc.
    background-color: red;
  }
}

-6

只需看屏幕尺寸即可。

@media (min-width: 550px) {
    .menu ul li:hover > ul {
    display: block;
}
}

-12

这是您要放入的代码

// a function to parse the user agent string; useful for 
// detecting lots of browsers, not just the iPad.
function checkUserAgent(vs) {
    var pattern = new RegExp(vs, 'i');
    return !!pattern.test(navigator.userAgent);
}
if ( checkUserAgent('iPad') ) {
    // iPad specific stuff here
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.