96

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

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

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

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

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

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

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

4

16 回答 16

76

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

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

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

并在“.no-touch”下方使用“:hover”的所有 CSS 规则:

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

在页面的某处,我有 javascript从正文中删除非触摸类。

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

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

于 2011-01-19T10:22:22.843 回答
44

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

iOS中触摸事件示意图

mouseenter并且mouseleave似乎包括在内,尽管它们没有在图表中指定。

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

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

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

于 2013-09-20T08:32:26.697 回答
22

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

像这样:

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

iOS Safari支持它

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

于 2016-11-15T19:08:04.680 回答
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">
于 2013-01-29T23:54:35.203 回答
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 中已修复,那么我可以修改测试。

于 2014-09-14T19:45:11.900 回答
16

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

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

FastClick.attach(document.body);

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


使用 FastClick 有几个小后果,这些后果可能对您的网站很重要,也可能无关紧要:

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

基本上有三种情况:

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

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

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

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

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

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

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

于 2014-12-28T20:36:47.007 回答
1

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

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

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

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

于 2011-07-08T18:49:23.527 回答
0

我创建了一个处理触摸事件的系统,而不是仅在触摸不可用时具有悬停效果,这为我解决了问题。首先,我定义了一个用于测试“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.
            });
于 2012-06-21T01:50:14.520 回答
0

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

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });
于 2013-11-20T12:52:53.913 回答
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
    }
});

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

于 2014-06-03T05:58:15.983 回答
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)" />

于 2019-04-07T07:15:10.127 回答
0

对于那些在 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;
  }
}
于 2020-08-07T19:48:34.230 回答
0

如果上述方法均无效,对于仍在寻找解决方案的人,

试试这个,

@media (hover: hover)
{
    .Link:hover
    {
        color:#00d8fe;
    }
}

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

于 2021-01-01T10:07:52.087 回答
-6

只看屏幕大小......

@media (min-width: 550px) {
    .menu ul li:hover > ul {
    display: block;
}
}
于 2015-03-19T08:49:00.017 回答
-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
}
于 2010-06-09T08:38:07.100 回答