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

Posted

技术标签:

【中文标题】是否可以强制忽略 iPhone/iPad 用户的 :hover 伪类?【英文标题】:Is it possible to force ignore the :hover pseudoclass for iPhone/iPad users? 【发布时间】:2011-02-14 01:41:09 【问题描述】:

我的网站上有一些用:hover(没有js)扩展的css菜单

这在 iDevices 上以半损坏的方式工作,例如点击将激活 :hover 规则并展开菜单,但随后点击别处不会删除 :hover。此外,如果元素内有一个链接是:hover'ed,您必须点击两次才能激活链接(第一次点击触发:hover,第二次点击触发链接)。

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

问题在于,有时移动 safari 仍然选择从 css 触发:hover 规则而不是我的touchstart 事件!

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

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

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

【问题讨论】:

请不要仅仅因为存在“触摸”事件而禁用悬停。这将对具有触摸和鼠标输入设备的笔记本电脑用户产生不利影响。更多详情见我的回答 【参考方案1】:

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

目前,我在 body 上只有一个“无接触”课程。

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

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

.no-touch my:hover
   color: red;

在页面的某处,我有 javascript 从正文中删除 no-touch 类。

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

这看起来并不完美,但它仍然有效。

【讨论】:

这是唯一的方法。困扰我的是,它用不属于那里且无关紧要的东西污染了“正常”网站。那好吧。谢谢。 您还可以使用带有 IE 后备的媒体查询 @Shackrock:我不认为“一键悬停,二点击”的方法是一个好的解决方案。由于实际上在触摸设备上没有实际悬停(直到它们带有感应您的手指悬停在其上的屏幕),我认为最好不要模拟悬停。对于效果,如按钮和链接上常见的效果,只需跳过效果即可。对于在悬停时展开的菜单,请改为在点击/单击时展开。我的 2c。 我最近在这种方法中发现了一些问题,因为新的 Windows 8 系统具有触摸和鼠标输入功能 +1 对 Tom 的评论 - 检查 ontouchstart 将在具有触控功能的笔记本电脑上返回 true,即使插入外接显示器也是如此(在这种情况下,更需要具有悬停状态的鼠标友好型网站)。跨度> 【参考方案2】:

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

mouseentermouseleave 似乎包括在内,但图表中未指定它们。

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

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

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

【讨论】:

当然 :hover 是问题 :-) 或者更确切地说是 Safari 的错误实现。这是这个问题的有趣背景,但苹果是唯一可以解决这种可怕行为的一方。当点击元素之外的东西时,它要么需要“取消悬停”,要么首先从不“悬停”。当前的行为基本上破坏了任何使用 :hover 鼠标的网站 这确实帮助我意识到 jquery hover 处理程序正在阻止单独的 click 处理程序被击中 - 真是个笑话! 出色的答案,以及对我遇到的问题的最佳解释。导致“双击”问题的不仅仅是 CSS 悬停,还有 mouseover/mouseenter 等事件之后的任何 DOM 修改。 这里是一个例子,它显示了在mouseenterjsbin.com/maseti/5/edit?html,js,output上修改DOM时点击事件没有触发 @Oliver Joseph Ash Right,因此答案中的 JS 示例。 ;)【参考方案3】:

更好的解决方案,无需任何 JS、css 类和视口检查:可以使用 Interaction Media Features (Media Queries Level 4)

像这样:

@media (hover) 
  // properties
  my:hover 
    color: red;
  

iOS Safari supports it

更多关于: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/

【讨论】:

虽然 Firefox 不支持(当然是在发表此评论时) Firefox 现在也支持此功能(FF 64+,2018 年 12 月发布)。 接受的答案来自 2011 年,这是 2021 年更好的方法。developer.mozilla.org/en-US/docs/Web/CSS/@media/hover【参考方案4】:

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

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

如果没有启用触摸事件 Modernizr 可以添加no-touch的类:

<html class="no-touch">

然后使用此类限定悬停样式:

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

您可以download a custom Modernizr build 包含您需要的尽可能少或尽可能多的特征检测。

下面是一些可以应用的类的例子:

<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">

【讨论】:

请注意,在桌面版 Chrome 的最新版本中,Modernizr 会报告“触摸”。这显然在版本 3 中通过 touchevents 得到纠正。【参考方案5】:

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

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

这是 Safari 中的一个错误,这种可怕的行为绝对没有任何理由。我拒绝破坏非 iOS 浏览器,因为 iOS Safari 中的一个错误显然已经存在多年。我真的希望他们下周为 iOS8 解决这个问题,但与此同时......

我的解决方案

有些人已经建议使用 Modernizr,Modernizr 允许您创建自己的测试。我在这里所做的基本上是将支持:hover 的浏览器的想法“抽象”到Modernizr 测试中,我可以在整个代码中使用它,而无需在整个代码中硬编码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 时消除悬停闪烁)并确保没有它也能正常工作。 谢谢。我同意。很想知道 iOS 9 为这个问题做了什么——如果有的话。 我走了相反的方向:html:not(.no-hover) .category:hover ... +1 表示“Safari 不会向世界‘宣布’它在使用 :hover 时表现不佳”在我看来,这个问题使得 iOS 比任何版本的 IE 都更糟糕......【参考方案6】:

将FastClick library 添加到您的页面会导致移动设备上的所有点击都变成点击事件(无论用户点击的位置如何),因此它还应该解决移动设备上的悬停问题。我编辑了你的小提琴作为例子:http://jsfiddle.net/FvACN/8/。

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

FastClick.attach(document.body);

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

使用 FastClick 会产生一些对您的网站可能无关紧要的小后果:

    如果您点击页面上的某处,向上滚动,向下滚动,然后在您最初放置它的完全相同的位置松开手指,FastClick 会将其解释为“点击”,即使它显然不是。至少在我当前使用的 FastClick 版本 (1.0.0) 中它是这样工作的。自该版本以来,有人可能已经解决了这个问题。 FastClick 移除了某人“双击”的能力。

【讨论】:

你愿意分享另一个具体的例子来说明我如何为我的网站做到这一点。我对javascript一无所知,所以我对它的工作原理有点困惑。我的网站上有所有这些链接,用户通常在点击之前将鼠标悬停在上面,但对于 ipad/mobile,我想这样做,这样他们就不必点击两次。 @Andy - 目前还不清楚您需要什么以及缺少什么......到目前为止,您尝试了什么,您看到了什么错误,如果有的话?也许最好创建一个新的 *** 问题,如果您还没有,并举例说明您正在尝试做什么(?)或者尝试澄清上面的 jsfiddle 示例中的内容,您没有不明白。 可以在单页应用中使用吗?我的意思是当 DOM 内容每次都更新时 是的,我在单页应用程序中使用它。【参考方案7】:

基本上有三种情况:

    用户只有有鼠标/指针设备并且可以激活:hover 用户只有有触摸屏,不能激活:hover元素 用户同时拥有触摸屏和指针设备

如果只有前两种情况是可能的,那么最初接受的答案效果很好,用户有任何一个指针或触摸屏。当 OP 在 4 年前提出这个问题时,这很常见。一些用户指出,Windows 8 和 Surface 设备使第三种情况更有可能发生。

iOS 解决无法悬停在触摸屏设备上的问题(如 @Zenexer 详述)很聪明,但可能导致简单的代码行为不端(如 OP 所述)。仅对触摸屏设备禁用悬停意味着您仍然需要编写一个对触摸屏友好的替代方案。检测用户何时同时拥有指针和触摸屏会进一步混淆水域(如@Simon_Weaver 所解释)。

此时,最安全的解决方案是避免使用 :hover 作为用户与您的网站交互的唯一方式。悬停效果是指示链接或按钮可操作的好方法,但不应要求用户悬停元素以在您的网站上执行操作。

Re-thinking “hover” functionality with touchscreens in mind 对替代 UX 方法进行了很好的讨论。那里的答案提供的解决方案包括:

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

展望未来,这可能是所有新项目的最佳解决方案。公认的答案可能是第二好的解决方案,但请务必考虑也具有指针设备的设备。当设备具有触摸屏只是为了绕过 iOS 的 :hover hack 时,请注意不要消除功能。

【讨论】:

【参考方案8】:

jQuery 版本 在您的 .css 中使用 .no-touch .my-element:hover 对于所有悬停规则 包括 JQuery 和以下脚本

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

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

一旦 ontouchstart 触发所有悬停状态的类就会被移除

【讨论】:

【参考方案9】:

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

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.
            );

【讨论】:

【参考方案10】:

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

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

【讨论】:

【参考方案11】:

鉴于 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
    
);

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

【讨论】:

【参考方案12】:

我同意禁用悬停触摸是可行的方法。

但是,为了省去重写 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)" />

【讨论】:

【参考方案13】:

对于那些在 iOS Safari 上禁用:hover 事件的常见用例,最简单的方法是对您的: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;
  

【讨论】:

【参考方案14】:

如果以上方法都不起作用,对于仍在寻找解决方案的人,

试试这个,

@media (hover: hover)

    .Link:hover
    
        color:#00d8fe;
    

此悬停伪指令仅适用于具有指针的设备,并且在仅具有 .active 类的触摸设备上正常工作。

【讨论】:

【参考方案15】:

只看屏幕大小....

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


【讨论】:

【参考方案16】:

这是您要放置的代码

// 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

【讨论】:

以上是关于是否可以强制忽略 iPhone/iPad 用户的 :hover 伪类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中区分 iPhone、iPad 和 iPod?

检测 iPhone/iPad/iPod touch 的颜色?

检测 iPhone/iPad/iPod touch 的颜色?

iphone/ipad : 如何通过 iOS 应用程序执行 Apple 安全策略?

在 iPhone / iPad 中画线

iPhone iPad 蓝牙传输