[关闭]
@EncyKe 2018-02-23T14:16:34.000000Z 字数 2330 阅读 3111

手记:iOS 设备移动页面触摸 click 事件

#手记



1. 缘起

在做一个移动页面上的汉堡下拉菜单时,遇见了一个 bug。

理想的状态应该是:点击汉堡按钮 => 出现背景蒙层,菜单下拉 => 点击空白处或蒙层 => 菜单回收。

Created with Raphaël 2.1.2开始点击汉堡菜单出现背景蒙层,菜单下拉用户操作:使用菜单?yes: 点击菜单链接no: 点击空白处或蒙层去往新页面菜单回收yesno

笔者的实现思路:

  1. 菜单默认隐藏;
  2. 汉堡按钮绑定了 click 事件,被触发时给菜单赋一个显示的动画类;
  3. body 上绑定 click 事件,被触发时删去上述动画类,回收菜单。

在控制台下模拟移动端调试 okay,笔者自家华为的真机测试也 okay,然而身为 iPhone 用户的 CTO 却不让 okay,因为在 iOS 设备下,点击空白处或蒙层(也就是 click 冒泡到 body )时回收菜单失效。

2. 移动设备 click 事件 300ms 延迟的锅?

鉴于此 bug 出于 iOS 设备,笔者想起了水果家有名的 300ms 延迟问题。

300ms 延迟的起源在于,移动设备有双击缩放页面的机制,而如何实现这一机制呢?iOS 当年的解决方案是将单击事件延迟 300ms,300ms 过后如若用户没有下一次单击,说明并非双击操作,此时再按 click 事件处理。

这算是一个历史遗留问题,在那个 iPhone 在刚刚横空出世的年代,区区 300ms 的点击延迟应该说是瑕不掩瑜。但在现在,在 2016 年,在硬件性能不断强大以及用户不断被惯坏的前提下,300ms 的延迟会给开发者带来一些头疼的问题。解决方案当然是有的,可参考附文。而其中一个最简单粗暴的方法就是禁用用户缩放

  1. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

因为 300ms 的原意就是缩放页面,那么在用户缩放功能被禁用之后,自然就不会存在这样的延迟了。

那么回到问题上来,菜单无法被回收的问题与 300ms 的关系大不大呢?笔者一路追溯查看笔者的网页的 head 标签,上面的代码段赫然在列,而且从逻辑上讲,点击无效跟点击延迟也是不同的,所以基本可以肯定这个 bug 并非 300ms 的锅。

3. iOS 设备 click 事件无法冒泡到 body 的锅!

经过搜索,另一个可能是因为 click 事件在 iOS 设备中无法冒泡到 body/document/window

对于这一 bug,颜海镜 在其博文中指出,水果家凡 safari 内核的设备都会出现。但在笔者测试的过程中,除了已知的移动设备确实有 bug 外,笔者的 Safari 9.0.3 (OSX 10.10.5) 倒是可以检测到 click 事件冒泡到 body,看来水果家对于 Mac 端的 Safari 已做了修复。

同时在 jQuery 的官方文档 中也提到了这个问题并提供了解决方案:

On mobile iOS (iPhone, iPad and iPod Touch) the click event does not bubble to the document body for most elements and cannot be used with .live() without applying one of the following workarounds:
- Use natively clickable elements such as a or button, as both of these do bubble to document.
- Use .on() or .delegate() attached to an element below the level of document.body, since mobile iOS does bubble within the body.
- Apply the CSS style cursor:pointer to the element that needs to bubble clicks (or a parent including document.documentElement). Note however, this will disable copy\paste on the element and cause it to be highlighted when touched.

回到问题本身,整理现有的解决方案如下:

3.1. 针对点击的元素:使用原生可点击的元素

3.2. 针对点击的元素:追加 cursor:pointer

3.3. 针对冒泡到的元素:避免指定到 body

3.4. 针对事件本身:改 clicktouch 事件


附:参考

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注