恼人的移动端双击链接问题

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

前几天,我们在 ShopTalk 上遇到一个关于 iOS 上普通锚链接的问题,出现了一种奇怪的情况,你无法只点击一次链接就跳转,需要点击两次才能跳转。我自己也遇到过这种情况,并且一直感到困惑。

我首先想到的是,可能发生了一些奇怪的、意料之外的 JavaScript 代码。也许是第一个点击的点击处理程序使用了 preventDefault(),然后被移除。但我没有发现任何类似的事情发生。我确定我还尝试了一些其他的方法,但最终放弃了,并使用了 FastClick 来确保链接点击正常工作。FastClick 的目的并不是解决这个问题,它更多的是解决一些移动浏览器在点击链接时带来的 300 毫秒延迟,这样它们可以等待查看你是否要进行双击(注意:这个问题不像以前那么严重了)。虽然不是最合适的工具,但它确实有效。

问题是,这根本不是 JavaScript 问题,而是一个 CSS 问题。

Nicholas C. Zakas 很久以前就记录了这个问题

苹果公司的人可能过于聪明了。他们意识到网络上有很多功能依赖于悬停状态,因此他们想出了一个方法来处理 iOS 上的 Safari 浏览器。当你触摸网页上的一个项目时,它首先会触发一个悬停状态,然后触发“点击”。最终结果是,你最终会看到使用 :hover 选择器应用的样式,持续一秒钟,然后才会发生点击交互。这有点令人吃惊,但它确实有效。因此,iOS 上的 Safari 非常关注选择器中包含 :hover 的样式。它们不会被简单地忽略。

(感谢 Pete Droll 指出这一点。)

以下两行 CSS 代码会导致此问题

a::after {
  display: none;
  content: "pseudo block!";
}
a:hover::after {
  display: inline;
}

在带有光标指针的浏览器中,你会看到伪元素在 :hover 时显示出来。

但是点击该链接不会阻止链接被访问。然而在 iOS 上,点击链接只会显示伪元素。需要再次点击才能实际跳转到链接。

Android 似乎不会出现这种情况。它会快速显示伪元素,但也会像往常一样跳转到链接。

给链接添加伪元素这种做法并不常见,对吧?我想这是真的,它并不常见。我想这就是为什么它不像我们想象的那样广为人知,并且只是偶尔会困扰人们的原因。

我见过有人使用伪元素来做一些美学上的事情,比如给文本添加更受控的下划线。所以如果这种情况只发生在 :hover 上,那么麻烦就来了。顺便说一下,它确实只发生在悬停状态下,而不是焦点或激活状态下。

2019 年 12 月更新:此处测试演示(在 Safari/iOS 13 中),伪元素似乎会短暂显示,然后就会跳转到链接,不需要双击。我还没有在各个移动平台上进行更深入的测试。

不仅仅是伪元素

任何子元素都会出现这种情况。请记住,这样做的初衷是处理仅在悬停时显示其他内容的情况。实际使用元素可能更为常见。

例如

...
<li>
  
  <a href="#0">I'm a thing in a list</a>

  <span class="controls">
    <button>Do Something</button>
  </span>

</li>
...
li .controls {
  visibility: hidden;
}
li:hover .controls {
  visibility: visible; 
}

将鼠标悬停在列表项上会显示一些控件。由于元素现在具有一个悬停状态,该状态会显示内容,因此它会阻止锚链接通过单次点击工作。

它不必是父元素,它可以是链接本身。

<a href="http://link.com">
  Link
  <span>Extra Stuff</span>
</a>
a span {
  display: none;
}
a:hover span {
  display: inline-block;
}

媒体查询帮助

人们很容易想……好吧,我只需在“桌面”网站上应用这些悬停效果,并选择一个媒体查询,例如……

@media (min-width: 500px) {
  a span {
    display: none;
  }
  a:hover span {
    display: inline-block;
  }
}

……这在简单的测试中有效,但浏览器窗口宽度并不是测试你是否具有光标和“正常”悬停效果的完美方法。

幸运的是,有一个针对指针的媒体查询可能对我们有用。

@media (pointer: fine) {
  a span {
    display: none;
  }
  a:hover span {
    display: inline-block;
  }
}

酷。

还有一个 规范 用于直接的悬停媒体查询。

@media (hover) {

}

这两种媒体查询样式在我的 Chrome 和 Safari 浏览器中都有效,但在 Firefox 中无效(支持级别图表),这使得它使用起来有点冒险。我听说,即使是用于检测触摸的 JavaScript 方法也很可疑,并且在支持两种输入方式的设备上总是会出问题。

2019 年 3 月更新:Firefox 64 于 2018 年 12 月发布,其中包含对悬停媒体查询的支持,这使得该支持板变得更好。这可能是这里最好的解决方案。

最好不要依赖悬停来显示任何内容

解决此问题的技术尚未成熟。

如果有任何方法,请设计你的网站,以便需要点击或轻触才能显示其他内容,但要尽可能清楚地说明这一点,并且不要将正常的、不希望被包含在内的链接困在这些元素内部。

Trent Walton 可能 六年前 就说对了。

最终,我认为悬停状态的消失会使网络变得更美好。简洁的内容、清晰的交互和简单的设计从未有过替代品。如果我们专注于使网络浏览变得更棒的核心元素,那么无论人们如何使用我们的网站,我们的网站都将正常运行。

演示

这里有一个你可以玩玩。