丁香园 F2E

花厂设计招待所

延迟,让你更幸福了吗?

做前端开发的同学应该知道:在移动端使用了 WebKit 内核的浏览器上,单击操作会延迟 300ms(也有延迟 350ms 的说法,下文统称为 300ms 延迟)后再执行。

造成延迟的原因

故事要从 2007 年第一场雪说起,当时的网站都是为大屏幕设备所设计的,苹果公司在发布首款 iPhone 前夕,遇到一个问题: 在用户使用像 iPhone 这种小屏幕设备的浏览器访问网站时,如何确保用户获得较好的浏览体验。苹果的工程师们做了一些约定来解决这个问题,其中一项约定便是双击缩放(double tap to zoom):用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。

由于当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300ms,以判断用户是否再次点击了屏幕。

逐渐地,几乎现在所有的移动端浏览器都支持了了双击缩放这个交互。

在人们还在为通过移动设备上网而惊叹的时期,没有人会在意这 300ms 的延时。而当移动互联网的浪潮滚滚而来时,对于 Web 开发人员来说,点击事件的快速响应变得非常重要。移动端浏览器上所有的单击事件都有 300ms 延迟这件事,开始被人们诟病。

解决延迟的方法

有问题,就会有对应的解决办法,任何一件事都逃不出这个定律。300ms 延迟也不例外。

常用的解决方案如下:- 禁用缩放- 更改浏览器视口的默认宽度- 指针事件 (Pointer Events)- FastClick

禁用缩放

这是一个比较容易想到的解决方案:既然延迟的起因是页面缩放,那就禁止页面缩放好了。

代码实现起来也很容易,只需要在 html 页面头部添加如下代码:

<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,minimum-scale=1,maximum-scale=1">

自古以来,“一刀切”是能解决问题的,但通常不是问题的最优解。

这个解决方案看似完美,但也带来一个明显的缺陷 —— 你必须完全禁用缩放来达到目的,而从移动端站点的可用性和可访问性来看,缩放是相当关键的一环。你很可能已经遇到过这个问题,即你想要放大一张图片或者一段字体较小的文本,却发现无法完成操作。

更改浏览器视口的默认宽度

2014 年,Chrome 开发团队宣布,在 Chrome 32 这一版中,他们将在通过<meta>标签设置 width=device-width 或者置为比 viewport 值更小的页面上禁用双击缩放。页面禁用了双击缩放,就意味着没有了 300 ms点击延迟。

这个方法代码实现起来依旧很简单:只需要在 html 页面头部添加一行代码:

<!--使浏览器将视口大小设为设备本身的尺寸-->
<meta name="viewport" content="width=device-width">

该方案只是禁用了双击缩放操作,用户依旧可以使用双指缩放(pinch to zoom)等操作与页面发生交互。缩放功能并非被完全禁用,也就不存在可用性和可访问性的问题了。

目前,主流的移动端浏览器均已支持该方案。

iOS 10 发布之后,通过 <meta> 标签设置的 user-scalablemin-scalemax-scale 会被 WebKit 忽略掉,这意味着 iOS 10 上的 Safari/Chrome 允许用户在每个页面上进行缩放。但是依旧可以通过使用 <meta name="viewport" content="initial-scale=1.0, width=device-width"> 来消除延迟,从而快速响应用户的单击操作。

在 iOS 10 的 hybrid 应用中,可以通过在 WKWebViewConfiguration 上设置新属性来阻止用户缩放:

 var ignoresViewportScaleLimits : Bool

默认值为 false ,这意味着 WKWebView 内容将允许内容阻止缩放。这保留了旧版本的iOS的行为。

实际上,正是因为 iOS 10 中的 Safari 和 SafariViewController 将值设置为 true,才会导致禁止页面缩放失效。

指针事件 (Pointer Events)

除了使用 HTML 的 <meta> 标签禁止页面缩放外,我们还可以通过 CSS 的方式来让页面快速响应点击操作。该种方式在国内通常被称作:指针事件。

指针事件最初由微软提出。指针事件是一个新的 web 事件系列,相应的规范旨在使用一个单独的事件模型,对所有输入类型,包括鼠标 (mouse)、触摸 (touch)、触控 (stylus) 等,进行统一的处理。

指针事件方案中,有一个和点击延迟直接相关的实现:一个名为 touch-action 的新 CSS 属性。根据规范 touch-action 属性决定 “是否触摸操作会触发用户代理的默认行为。这包括但不限于双指缩放等行为”。

默认情况下,页面上的元素具有 touch-action: auto;,这表示 WebKit 可以启用任何触摸行为,例如平移,捏和双击。当页面上的元素具有 touch-action: manipulation; 时,WebKit 仅会支持在可点击元素上的触摸行为,仅用于平移和缩放的目的。 这味着 WebKit 不会考虑对元素执行双击手势,因此会立即调度单个点击。 当任何元素的祖先具有touch-action: manipulation; 时,元素上的单个点击变得快速。 注意,页面上所有具有了touch-action: manipulation;的节点的子节点都会立即调度单个点击事件。该种方式适用于各种缩放比例的页面。

关于 touch-action 具体详情,可访问W3C 规范的候选推荐标准阶段进行了解。

可以从http://caniuse.com/来了解现阶段各个浏览器对touch-action的支持情况。

指针事件的 polyfill

下面列出了几种针对指针事件的 polyfill:

需要注意的是,如果你需要的仅仅是一个解决 300ms 点击延迟的方法,上述方案可能不是最理想的。因为它们要么是会影响页面整体性能的资源密集型的方案,要么是 touch-action 属性的非标准化模拟。

FastClick

FastClick 是 FT Labs 专门为解决移动端浏览器 300 ms点击延迟问题所开发的一个轻量级的库。

实现原理

FastClick 在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即触发一个模拟 click 事件,并把浏览器在 300 ms之后真正触发的 click 事件阻止掉。

使用方法

FastClick 的使用方法非常简单。官方推荐的使用方法为:当初始 HTML 文档已完全加载和解析时,调用 FastClick.attach(document.body);

if ('addEventListener' in document) {
  document.addEventListener('DOMContentLoaded', function() {
    FastClick.attach(document.body);
  }, false);
}

如果使用了 jQuery,可以使用如下方式使用 FastClick:

$(function() {
  FastClick.attach(document.body);
});

如果使用了 npm 等模块化的方式安装了 FastClick,则可以按照如下方式使用:

const attachFastClick = require('fastclick');
attachFastClick(document.body);

其实具体如何使用 FastClick,去看一下 FastClick 的源码就一目了然了:

if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {

  // AMD. Register as an anonymous module.
  define(function() {
    return FastClick;
  });
} else if (typeof module !== 'undefined' && module.exports) {
  module.exports = FastClick.attach;
  module.exports.FastClick = FastClick;
} else {
  window.FastClick = FastClick;
}

值得注意的是:attach() 方法虽可在更具体的元素上调用,直接绑定到 <body> 上可以确保整个应用都能受益。当 FastClick 检测到当前页面使用了基于 <meta> 标签或者 touch-action 属性等解决方案时,会静默退出。可以说,这是真正的跨平台方案出来之前一种很好的变通方案。

移动端浏览器的现状

目前 iOS 和 Android 两大平台的移动设备上的浏览器,以及两个平台上微信中的内置浏览器均可通过上述方案来解决 300 ms 延时问题。

备注

目前各个浏览器使用的内核情况。

浏览器内核
SafariWebKit
ChromeWebKit
OperaWebKit
FirefoxGecko
国产浏览器极速模式WebKit
微信浏览器WKWebView

参考

原创
下一篇:关于 vertical-align的一些试验