移动端 click 事件 300ms 延迟的前世今生

原文首发于 baishusama.github.io,欢迎围观~

存疑

最开始,我遇到的其实是“移动端遮罩层滑动穿透”的问题。

在查找“滑动穿透”问题相关资料的时候,我搜到了很多 click 300ms 延迟的问题。我那个时候有些不知所云,因为我自己并没有真实遇到过 300ms 延迟现象,也就没怎么在意。

时至今日想动笔总结遇到了若干次的“滑动穿透”问题的时候,搜集资料的偶然间得以解惑 300ms 的前世今生。

移动端 click 的 300ms 延迟

那么,这 300ms 延迟到底是从哪里来的呢?

时间要追溯到 2007 年初代 iPhone 发布前夕,苹果为了解决“如何用手机这种小尺寸屏幕来显示 PC 端网页”这个问题,提出了很多聪明的约定(convention)。而后因为 iPhone 的大获成功,这些约定被各大手机浏览器争相效仿。

这些约定之中,双击缩放(Double Tap to Zoom) 就是 300ms 的“元凶”——当用户在页面上 click 的时候,浏览器为了判断这个用户操作是单击还是双击,会等待 300-350ms 。如果 300ms 内,发生了第二次 click 事件,那么视为双击;否则为单击,等 300ms 时间过去之后,才触发 click 事件。

在那个还不存在响应式设计和双指缩放(Pinch to Zoom)的时代,这个延迟是一个合理的预防措施。但不幸的是,这 300ms 的延迟已经成为用户觉得 web 应用比 native 应用更慢、性能不及后者的主要原因之一。诸如,链接、按钮、多选框等基于 click 交互的元素,以及 JS 对 click 事件的监听,都因此受到影响。

幸运的是,浏览器开发商(vendor)和开发者都注意到了这个问题,提出了一些解决方案。

解决方案

方案一、👎 禁用缩放

  • 代码:
    <meta name="viewport" content="user-scalable=no">
    <!-- 或者 -->
    <meta name="viewport" content="initial-scale=1, minimum-scale=1, maximum-scale=1">
    
  • 原理:双击是为了缩放,如果禁用缩放,那么就没双击什么事儿了,也不需要额外等待 300ms 了。
  • 支持情况:在 Android 平台上,由 Chrome 最先提出,FireFox、Opera 等浏览器也相继支持;IOS 9.3 开始一度支持,IOS10 开始不再支持。
  • 缺点:Safari 不支持。而且,禁用缩放会损害移动端网页的可用性和可访问性。例如,可能无法放大网页中的一张图片或一段字体较小的文字。

这里要注意区分:“双击缩放”(Double Tap to Zoom)和“双指缩放”(Pinch to Zoom)。为了兼顾消除 300ms 延迟和不损害可用性和可访问性,我们应该抛弃双击缩放、拥抱双指缩放。

方案二、👍 视窗宽度设置为设备宽度

  • 代码:
    <meta name="viewport" content="width=device-width">
    
  • 原由:正如 Chromium Code Reviews 上说的,viewportwidth 设置得小于等于 device-width 的页面,是针对移动端优化过的或者是响应式的站点,其内容足够清晰,双击缩放失去了意义。因此,为包含上面这行代码的页面禁用双击缩放。同时,双指缩放得以保留,从而也就没有可用性和可访问性问题了。
  • 支持情况:自 Chrome 32 开始,FF、IE/Edge 也随后支持了;2016 年 3 月,IOS 9.3 开始支持。
  • 推荐使用!

该解决方案的“禁止双击缩放”是遵守如下规则的:

  • 当页面设置了视窗宽度为设备宽度且是初始尺寸(页面尚未缩放),此时,双击缩放才是被禁止的。
  • 如果视窗尺寸不是初始尺寸(页面已经缩放),双击缩放是被允许的。
  • 为了在用户结束缩放后仍能 fast-click ,缩小时,只能缩小到初始尺寸,而不是最小尺寸。

方案三、👍 指针事件(Pointer Events)

  • 代码:
    a, button, .myelements {
        -ms-touch-action: manipulation; /* IE10  */
        touch-action: manipulation;     /* IE11+ */
    }
    
  • 根据规范:CSS 属性 touch-action 决定了触摸输入(touch input)能否触发 UA (User Agent)支持的默认行为。这包括但不限于诸如平移或缩放等行为。
  • 根据 MDNtouch-actionmanipulation 值激活了平移和双指缩放手势,而禁用了双击缩放等非标准的手势。
  • 支持情况:在 Can I Use 上可以看出,除了 Opera Mini 不支持、FF 需要手动启用和 Android 4.x 的自带浏览器有些迷之外,其他浏览器支持良好。
  • 推荐使用!

在只有 IE 支持指针事件的初期,诞生了不少指针事件的 polyfill 解决方案。在仍不支持指针事件的浏览器上,这是一种变通的方式。

shim VS polyfill

  1. 一个 shim 是一个库,它将一个新的 API 引入到一个旧的环境中,而且仅靠旧环境中已有的手段实现。
  2. polyfill 就是浏览器 API 的 shim 。 它用于实现浏览器并不支持的原生 API 的代码,是抹平新旧浏览器对原生 API 支持差异的封装。通常,polyfill 会先检查当前浏览器是否支持某个 API,如果不支持的话就加载它自己的实现,然后新旧浏览器就都可以使用这个 API 了。相当于“打补丁”,“刮腻子”。

方案四、👍 轻量级库 FastClick

  • 代码:
    window.addEventListener( "load", function() {
        FastClick.attach( document.body ); // 直接绑定到 <body> 上可以确保整个应用都能受益
    }, false );
    
  • 原理:FastClick 在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即触发一个模拟的 click 事件,并把浏览器 300ms 之后真正触发的 click 事件阻止掉。
  • 无冲突:当 FastClick 检测到当前页面使用了基于 <meta> 标签或者 touch-action 属性的解决方案时,会静静地看别的解决方案装逼
  • 唯一的缺点:文件大小占 10 KB……
  • 推荐使用!

关于“始作俑者” Safari

起承转折

(2013) 300 毫秒点击延迟的来龙去脉一文中提到的 IOS 特有的双击滚动(Double Tap to Scroll):仍存在、并没有像原文猜测的那样消失。(亲测 IOS 10.2.1 Safari 已设置 <meta name="viewport" content="width=device-width"> 的页面在屏幕上或下 1/4 处双击仍能滚动。)

起初看到「2016 年 3 月发布的 IOS 9.3 移除了 300ms 延迟、从而实现了“fast-tap” 」时,我还欣慰地想道:最先提出“双击缩放”约定的苹果,在最后也顺应了历史潮流嘛。但是接着看到「IOS10 无视了禁用缩放(user-scalable)」我的内心瞬间黑人问号:“???”。

后来,静静地看了两篇文章(Safari zoom gesture's comeback in iOS 10How to disable viewport scaling in iOS 10? You don't.),做了点 <meta> 标签的测试。

测试结果

测试环境:IOS 10.2.1

  1. 只设置 <meta name="viewport" content="user-scalable=no"> 和不设置没有任何区别——user-scalable=no 被完全无视。
  2. 只设置 <meta name="viewport" content="width=device-width"> ,和方案二里的描述一致,仍可以在初始尺寸下禁用双击缩放。
  3. 只设置 <meta name="viewport" content="initial-scale=1.0"> ,初始状态和“测试2”很像,但是仍存在双击缩放,即仍有 300ms 延迟。
  4. 设置 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 或者 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> ,和只设置 width=device-width 并无显著差异。
  5. 为某个 <a> 链接设置 touch-action: manipulation; ,可以禁用该元素上的双击缩放。

要点如下:

  1. user-scalable=no 完全起不到禁止缩放的作用,width=device-width 仍能且仅能禁止双击缩放。
  2. 只设置 meta 无法完全禁用缩放,双指缩放总是可行的。

暮然回首

冷静下来后,重新审视上述变故,发现其实是两回事。前面提到过“我们应该抛弃双击缩放、拥抱双指缩放”,苹果没有打破这个原则。只是,苹果出于可访问性考虑,直接任性地完全无视了 user-scalable=no

Accessibility
Pinch-to-zoom is always enabled for all users. The viewport setting for user-scalable is ignored.

当然,这导致了觉得应该一切尽在掌控、想要完全禁用缩放以避免破坏布局的开发者的怨言。如果,你还是想完全禁用缩放,可以参考 SO 上的这个回答

解惑

最开始提到过,我至今没有遇到过这个问题。对这个现象我推理如下:

我的肾机在开发移动端的半年间只在近期做过一次系统升级(目前已升到 10.2.1)。之前使用的具体的版本号已经无从得知了(P.S. 如果有谁知道怎么查看肾机本机上的版本更新历史,请务必告诉我233),但是更新到 IOS10 之前,我一直有使用 9.3+ 才支持的 Night Shift 功能,也就是说升级之前的系统版本号肯定在 9.3 或者以上。

而我写移动端页面的时候,惯例会 meta:vp 然后 Tab 生成 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">。在 IOS10 之前,这行代码还是能够禁用页面的缩放的,也就不存在 300ms 的延迟问题了。

这就是为什么之前我本机测试的时候一直没有遇到传说中的 300ms 延迟现象的原因了。

参考

  1. (2013) 300 毫秒点击延迟的来龙去脉
  2. (2014) 5-ways-prevent-300ms-click-delay-mobile-devices
  3. (2015) Implement viewport-width-based fast-click heuristic
  4. (2016) 无线端浏览器 click 事件 300ms 延迟
  5. (2016) 300ms-tap-delay-gone-away
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容