2019 再聊挪动端 300ms 耽误及 fastClick 道理剖析

媒介

近来公司新开了一条营业线,有幸和大佬们一同从头最先构建一套合适新营业的框架。俗话说得好呀,合适自身的才是最好的 😎。在新项目的 CodeReview 的时刻,被老大提到有无增加 fastClick 处理挪动端 300ms 耽误的题目。以下就带你追溯挪动端耽误的 宿世 此生

引见

宿世 – 降生的因

外洋有一篇关于 300ms 耽误的文章:What Exactly Is….. The 300ms Click Delay

人间万物皆有因果,网页鼓起于桌面端,那时刻有谁会想到手机等挪动装备的风行?犹记得上大学那会儿,手机接见学校网站的时刻都是经由历程手指缩放来掌握的 🙃,内心真的是一万头草泥马奔驰而过,厥后为了处理挪动端适配的题目,提出了 viewport 的处理方案,基于 无障碍(accessibility)(须要代办)交互设想师为了更好的用户体验,专程供应了 双击缩放 的手势支撑。却不知这正是统统祸乱的泉源。

此生 – 消失的果

谷歌有开发者文档: 300ms tap delay, gone away(须要代办)

以下是原文的部份援用

For many years, mobile browsers applied a 300-350ms delay between touchend and click while they waited to see if this was going to be a double-tap or not, since double-tap was a gesture to zoom into text.

大抵是说,挪动浏览器 会在 touchendclick 事宜之间,守候 300 – 350 ms,推断用户是不是会举行双击手势用以缩放笔墨。

Ever since the first release of Chrome for Android, this delay was removed if pinch-zoom was also disabled. However, pinch zoom is an important accessibility feature. As of Chrome 32 (back in 2014) this delay is gone for mobile-optimized sites, without removing pinch-zooming! Firefox and IE/Edge did the same shortly afterwards, and in March 2016 a similar fix landed in iOS 9.3.

从上面我们能够获取到几个非常重要的信息:起首,谷歌就最先吹啦,自打我们挪动版 Chrome 宣布以来,只需你把缩放禁用掉,这个耽误就不会涌现。不能不吹一波 Google,真的是甩开 Apple 几条街,fastClick 源码大部份都是用来处理 iOS 各个版本种种奇奇怪怪的 BUG。说实话,有些源码我也不是很明白,然则咱啥也不敢说,啥也不敢问啊 😂。其次,Chrome 32 对挪动端举行了优化,能够不禁用缩放,也能处理耽误的题目。接着 Firefox 和 IE/Edge 紧随其后也修复了这个 BUG,末了,就是 iOS 9.3 也一样修复这个 BUG (亲测确实修复了)。

处理方案

以下能够经由历程 hack 技能,不增加 fastClick 也能修复耽误的题目

禁用缩放

  • Chrome on Android (all versions)
  • iOS 9.3
<meta name="viewport" content="user-scalable=no" />

或许

html {
  touch-action: manipulation;
}
  • IE on Windows Phone
html {
  touch-action: manipulation; // IE11+
  -ms-touch-action: manipulation; // IE10
}

不禁用缩放

  • Chrome 32+ on Android
  • iOS 9.3
<meta name="viewport" content="width=device-width" />

经测试,如果不增加 width=device-width 不管是 Android 照样 iOS 在已修复的版本中依然会涌现延时的题目。

WebView

上面说了这么多,都是针对挪动端浏览器的,既然是提到挪动端,WebView 固然不能不说啦。

Android WebView

怎样设想一个文雅硬朗的 Android WebView?

Android WebView 中 300ms 的耽误题目和挪动端浏览器处理思绪一致。

iOS WebView

UIWebView

In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView. Additionally, consider setting the WKPreferences property javaScriptEnabled to false if you render files that are not supposed to run JavaScript.

iOS WebView 就有点让人头疼了。由于 iOS 8 之前一向都是 UIWebView,iOS 8 出了个新秀 WKWebView,那末 iOS 9.3 300ms 耽误的 BUG 修复究竟干了啥呢?在客户端 iOS 小姐姐的协助下,终究的测试结果是 UIWebView 300ms 耽误的题目到现在一向存在,哪怕是最新的 iOS 版本(这也许这就是为何老外引荐运用 WKWebView 而非 UIWebView,预计是不想修 BUG 了吧 😂),然则 WKWebView 在 iOS 9.3 的时刻将这个题目给修复了。也就是说 iOS 9.3 之前 WKWebView 依然是存在 300ms 耽误的题目的(忙活了半天,总算把一切的都给理清晰了 🙄)。

FastClick 原明白析

这部份能够有点烂大街了,网上一搜一大把,再说也没啥意义,我就挑点个人以为有意义的说一下 😁。

道理

起首,讲一下 fastClick 的完成道理吧,MDN 上 同时支撑触屏事宜和鼠标事宜 也有提到。

挪动端,当用户点击屏幕时,会顺次触发 touchstarttouchmove(0 次或屡次),touchendmousemovemousedownmouseupclicktouchmove 。只要当手指在屏幕发作挪动的时刻才会触发 touchmove 事宜。在 touchstarttouchmove 或许 touchend 事宜中的恣意一个挪用 event.preventDefaultmouse 事宜 以及 click 事宜将不会触发。

fastClick 在 touchend 阶段 挪用 event.preventDefault,然后经由历程 document.createEvent 建立一个 MouseEvents,然后 经由历程 event​Target​.dispatch​Event 触发对应目的元素上绑定的 click 事宜。

你不知道的 JavaScript (Maybe)

起首,我们须要明白一个题目,300ms 的耽误只要在挪动端才会涌现,PC 端是没有的。fastClick 中又有个一 notNeeded 的函数是用来推断有无必要运用 fastClick。刚最先的时刻,刚最先我浏览完代码示意对没有举行挪动端和 PC 端的辨别示意不满。不过厥后一段不起眼的代码改变了我的意见。

// Devices that don't support touch don't need FastClick
if (typeof window.ontouchstart === 'undefined') {
  return true;
}

PC 端是没有 touch 事宜的因而 window.ontouchstart 返回 undefined,挪动端如果没有绑定事宜则返回 null。果真只能证实我照样太甚年青 🤣。

浏览源码时期,无意中发明运用事宜托付时,Safari 手机版会有一个 bug,当点击事宜不是绑定在交互式的元素上(比如说 HTML 的 div),而且也没有直接的事宜监听器绑定在他们自身。不会触发 click 事宜。详细能够参考 click 浏览器兼容性

处理要领以下:(请原谅我恬不知耻地直接搬过来了 😝)

  • 为其元素或许先人元素,增加 cursor: pointer 的款式,使元素具有交互式点击
  • 为须要交互式点击的元素增加 onclick="void(0)" 的属性,但并不包含 body 元素
  • 运用可点击元素如 <a>,替代不可交互式元素如 div
  • 不运用 click 的事宜托付。

event.stopPropagation 只会阻挠雷同范例(event.type 雷同)事宜流传,上面有提到过 挪动端 触摸事宜触发的递次题目,如果 我在 touchstart 中挪用了 event.stopPropagation 只会 阻挠后续 event flow 上其他 touchstart 事宜,并不会阻挠 touchmovetouchend 等 mouseEvent 事宜的发作。

event.stopPropagationevent​.stop​Immediate​Propagation的区分你真的知道吗 🧐,event.stopPropagation 阻挠捕捉和冒泡阶段中当前事宜的进一步流传。如果有多个雷同范例事宜的事宜监听函数绑定到同一个元素,当该范例的事宜触发时,它们会根据被增加的递次实行。如果个中某个监听函数实行 event.stopImmediatePropagation 要领,则当前元素剩下的监听函数将不会被实行。

event​Target​.dispatch​Event 依然会触发完全的 event flow,而不单单议触发 event​Target​ 自身注册的事宜。

总的来讲,浏览源码的历程是一次自我修炼的历程,是对过去某些不足的完美,实在就是发明自身很菜 😂,火线道险且长,同志们仍需勤奋呀 🤜。

不足

个人以为浏览优异的源码是一件很幸运的事,由于它能耳濡目染的提拔你的审美才能。但同时我们也要带有抉剔的眼力,找出当中存在的不足之处

fastClick 中 notNeeded 函数总的来讲,已相称不错了,然则美中不足的是,关于 iOS 9.3 以上 运用 WKWebView 的用户来讲,引入 fastClick 无疑是节外生枝,另有能够致使某些潜伏的题目。关于处女座的我来讲,这一点是不能忍耐的。不过纯真的经由历程 UA 是没法辨别 UIWebViewWKWebView 的。不过如果页面是在自身 App 中话,能够经由历程在 UA 中照顾 WebView的信息来决议是不是加载

耽误检测 code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style></style>
  </head>
  <body>
    <div>
      <label for="userAgent">userAgent:</label>
      <span id="userAgent"></span>
    </div>
    <div>
      <label for="touchstart">touchstart:</label>
      <span id="touchstart"></span>
    </div>
    <div>
      <label for="touchend">touchend:</label>
      <span id="touchend"></span>
    </div>
    <div>
      <label for="click">click:</label>
      <span id="click"></span>
    </div>
    <div>
      <label for="diffClickTouchend">diff click - touchend:</label>
      <span id="diffClickTouchend"></span>
    </div>
    <div>
      <div id="test">test</div>
      <div id="diff">diff</div>
    </div>
    <script>
      var userAgent = document.getElementById('userAgent');
      userAgent.innerText = window.navigator.userAgent;

      var test = document.getElementById('test');
      var diff = document.getElementById('diff');
      var touchstart = document.getElementById('touchstart');
      var touchend = document.getElementById('touchend');
      var click = document.getElementById('click');
      var diffClickTouchend = document.getElementById('diffClickTouchend');

      test.addEventListener('touchstart', function(e) {
        touchstart.innerText = Date.now();
      });

      test.addEventListener('touchend', function(e) {
        touchend.innerText = Date.now();
      });

      test.addEventListener('click', function(e) {
        click.innerText = Date.now();
      });

      diff.addEventListener('click', function() {
        diffClickTouchend.innerText = click.innerText - touchend.innerText;
      });
    </script>
  </body>
</html>

完毕语

回眸汗青,不可否认 fastClick 在处理挪动端 300ms 耽误的题目上确实作出卓越的孝敬,不过 9102 的本日,是不是依然有必要运用呢,回到最先,我说过,合适自身的才是最好的,因而,如果你的营业需求,是只须要对 iOS 9.3 以上的 WKWebView 做适配,那末强烈建议你不去运用,毕竟减少了文件要求大小,引入风险的几率。

末了,援用一句名言 老兵不死,只是落莫 向 fastClick 致敬。

参考 (References)

文 /
lastSeries

作者也在掘金哦,快关注他吧!

编 / 荧声

本文由创宇前端作者受权宣布,版权属于作者,创宇前端出品。 迎接说明出处转载本文。文章链接:https://juejin.im/post/5cdf84…

《2019 再聊挪动端 300ms 耽误及 fastClick 道理剖析》

本文是创宇前端相干账号末了一次由荧声担任。终究,我们一同走到了一个故事的完毕。

都知欢聚最难过,难奈分别多

感谢您的浏览,以及长期以来的支撑。

再会啦。

    原文作者:创宇前端
    原文地址: https://segmentfault.com/a/1190000019281808
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞