前几天,我们在 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 可能 六年前 就说对了。
最终,我认为悬停状态的消失会使网络变得更美好。简洁的内容、清晰的交互和简单的设计从未有过替代品。如果我们专注于使网络浏览变得更棒的核心元素,那么无论人们如何使用我们的网站,我们的网站都将正常运行。
演示
这里有一个你可以玩玩。
这是最好的建议。如果你想为使用鼠标的用户提供不错的悬停效果,我喜欢使用类似 Modernizr 的东西,将这些内容隐藏在
has-touch
标志后面。不幸的是,这种技术在大多数现代 Windows 设备上都会失效,这些设备混合了触摸、鼠标,而且通常还有笔输入。甚至 Android 手机和平板电脑以及 iPad 也支持鼠标输入,而且经常支持手写笔输入。人们经常混合使用输入模式——当我使用手写笔时,我经常用手指滚动。当拿起设备参加会议时,我可能会在鼠标和触摸之间切换。
我真不认为有任何“安全”的解决方案——所以,我同意你的观点,以及文章的观点,即最好的方法是避免使用悬停状态来显示任何内容。
如上所述,不幸的是,媒体查询尚未得到广泛支持。
我目前使用的是一个很小的 JavaScript 代码,它检查触摸支持和鼠标移动,对我来说效果很好。它仍然不完美,但比只关注触摸支持或只依赖目前尚未得到广泛支持的媒体查询要好。
此代码块会向 html 元素添加“touch”和“mouse”类,如果检测到触摸或鼠标。
http://codepen.io/anon/pen/JRLmyX
在我看来,处理此问题的最佳方法是在不依赖“悬停”的情况下构建网站,并为不支持触摸功能的设备“增强”功能 :)
例如,可以使用 Modernizr 检测不支持触摸功能的设备,这意味着
– 默认情况 => 无悬停效果,
– 具有触控功能的台式机 => 无悬停效果,
– 其他所有情况 => 悬停效果。
以下是一个示例 http://albinism.ohchr.org/
– 默认情况 => 无悬停效果(无颜色渐变),
– 具有触控功能的台式机 => 无悬停效果(无颜色渐变),
– 其他所有情况 => 悬停效果(正常)
无论如何,Apple 文档中有解释:https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW7
几天前我遇到了这个问题。我有一个图像和一些文本包裹在一个锚点中,当用户将鼠标悬停在图像上时,文本会显示在图像上,否则会使用
display: none
隐藏。我解决此问题的变通方法是使用opacity: 0
隐藏文本,并使用opacity: 1
显示文本。此方法可能不适用于所有场景,但可以适用于其中一些。我支持 Mauro 的评论:这种双击情况仅在链接内的元素从 display none 更改为其他 display 值时才会发生。
一个简单的解决方法是使用任何其他方法隐藏/显示此元素(opacity、visibility、position 等),而不是 display none。
无论如何,我同意这些显示的元素不应该是必要的元素,并且界面应该在没有它们的情况下也能理解,这样不使用鼠标的用户也不会束手无策。
为什么现在不是那么大的问题了?移动浏览器是否缩短了延迟或找到了解决方法?
在我的网站上,我能够通过以下方法解决此问题:而不是使用 width 和 height 来缩放链接内的伪元素,我使用 transform:scale(0); 然后在悬停时将其更改为 transform:scale(1);
这部分解决了我的问题,因为动画开始了,但 iOS 不会在加载下一页之前完成它。因此,我向按钮添加了一个媒体查询,该查询设置 transition:all 0s; 并且有趣的是,动画实际上在 iOS 上有效,尽管它更短,但实际上它不是 0s,只是比桌面上的短。
希望对某些人有所帮助,请查看我发布的链接并检查按钮以查看我指的是什么。
Andreas Deuschlinger 写信说,他发现
width
也会触发它。就像将子元素或伪元素设置为width: 0
类似于display: none
或visibility: hidden
并触发此“错误”。我必须想象height: 0
也是一个问题。很想看看通过绝对定位将元素踢出页面是否也会导致问题(我怀疑不会)。