是否有可能强制忽略:hover伪类用于iPhone / iPad用户?

问题描述 投票:91回答:14

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

这在iDevices上以半破碎的方式工作,例如tap会激活:hover规则并展开菜单,但是在其他地方点击不会删除:hover。此外,如果元素内部有:hover的链接,则必须点击两次以激活链接(首先点击触发:hover,第二次点击触发链接)。

通过绑定touchstart事件,我已经能够在iphone上很好地工作。

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

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

有没有办法在用户进行移动旅行时动态“取消”某些元素的:hover规则?

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

ipad mobile-safari iphone pseudo-class
14个回答
74
投票

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

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

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

并将所有CSS规则与“:hover”置于“.no-touch”下方:

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

在页面的某个地方,我有javascript从正文中删除无触摸类。

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

这看起来并不完美,但无论如何都可行。


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

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

42
投票

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

Diagram of touch event in iOS

mouseentermouseleave似乎被包括在内,尽管它们没有在图表中指定。

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

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

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


18
投票

浏览器功能检测库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">

17
投票

一些设备(正如其他人所说)同时具有触摸和鼠标事件。例如,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中修复了那么我可以修改测试。


16
投票

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

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

FastClick.attach(document.body);

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

使用FastClick会产生一些轻微的后果,这可能会对您的网站产生影响,也可能无关紧要:

  1. 如果您点击页面上的某个位置,向上滚动,向下滚动,然后将手指放在最初放置的完全相同的位置,FastClick会将其解释为“点击”,即使它显然不是。至少它是如何在我正在使用的FastClick版本(1.0.0)中工作的。从那个版本开始,有人可能已经解决了这个问题。
  2. FastClick删除了某人“双击”的能力。

11
投票

一个更好的解决方案,没有任何JS,css类和视口检查:您可以使用交互媒体功能(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/


4
投票

基本上有三种情况:

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

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

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

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

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

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

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


1
投票

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

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

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

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


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.
            });
© www.soinside.com 2019 - 2024. All rights reserved.