博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转] iOS11.3 fastclick.js相关bug
阅读量:6148 次
发布时间:2019-06-21

本文共 4893 字,大约阅读时间需要 16 分钟。

最近遇到奇异的bug,在ios 11.3移动端页面 input输入框第一次触摸可以弹起键盘,后续再触摸需要很难弹起键盘,或者需要在输入框停一会才能弹起键盘。

bug复现条件:

一、ios 11.3中app的webview为 UI WebView

二、在项目中使用了FastClick.js,页面包括输入框

发现源头问题:

在碰到问题脑子第一想法这不就是click延迟300ms的现象吗?所以就想到是不是FastClick.js导致,注释掉后发现bug现象消失了,代码如下:

define(['zepto'], function ($) { 'use strict'; // FastClick.attach(document.body); ... });

但是这是为什么呢?我们一起看看为什么要加上FastClick,这个库解决了什么问题?

  • click 300ms延迟:浏览器click会比touch延迟300ms触发
  • click穿透现象:当两个div同处一个position,上层div绑定touch,下层div绑定click,当上层div触发touch消失后,可能会触发下层div的click事件

    既然Fastclick是为了解决这两类问题,其实现原理如下图所示:

     
    fastclick原理

fastclick利用捕获顶层dom元素(如:body,html等)的click事件,拦截所有的click请求进行判断:是否有touch触发、是否需要阻碍click事件(stopImmediatePropagation)等。

分析问题解决方案:

步骤一:input无法聚焦弹出键盘,fastclick中有一块判断当前元素targetElement是否需要needsFocus,看看其方法的实现:

FastClick.prototype.needsFocus = function(target) { //判断当前元素是否需要focus switch (target.nodeName.toLowerCase()) { case 'textarea': return true; case 'select': return !deviceIsAndroid; case 'input': switch (target.type) { case 'button': case 'checkbox': case 'file': case 'image': case 'radio': case 'submit': return false; } // No point in attempting to focus disabled inputs return !target.disabled && !target.readOnly; default: return (/\bneedsfocus\b/).test(target.className); } };

步骤二:看到needsFocus下执行了什么?在touchEnd方法中,代码块如下:

if (this.needsFocus(targetElement)) {
if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) { this.targetElement = null; return false; } this.focus(targetElement); //调用focus进行聚焦 this.sendClick(targetElement, event); if (!deviceIsIOS || targetTagName !== 'select') { this.targetElement = null; event.preventDefault(); } return false; }

步骤三:focus方法分析(包含解决方案),如下:

FastClick.prototype.focus = function(targetElement) {        var length; //兼容处理:在iOS7中,有一些元素(如date、datetime、month等)在setSelectionRange会出现TypeError //这是因为这些元素并没有selectionStart和selectionEnd的整型数字属性,所以一旦引用就会报错,因此排除这些属性才使用setSelectionRange方法 if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month' && targetElement.type !== 'email') { length = targetElement.value.length; targetElement.setSelectionRange(length, length); /*修复bug ios 11.3不弹出键盘,这里加上聚焦代码,让其强制聚焦弹出键盘*/ targetElement.focus(); } else { targetElement.focus(); } };

原理分析

OK,上真机iphoneX验证bug已经消失了,但是我们并不知道为什么在ios 11.3会出现该问题,秉着探索真理的一颗心(ZZZZ),到github去查看FastClick的issues列表,果然发现早有人提出bug了,如下图:

 
fastclick issues

下方有评论如下:

A:说framework7框架那边已经有解决方案啦,。

 
frameword7问题解决

 

另外一位仁兄的解决方案和我类似,修改focus方法。

 
focus解决方案

因此跳到framework的issue中的解决方案,解决方案:,描述如下:

 
解决方案描述

 

跳过去stackoverflow后,其实根本源头已经查到了,ios 11.3更新 Safari 11.1,支持新web API :允许对事件支持 {passive: false}被动模式,减少滚动屏幕的性能损耗和奔溃。

passive mode解析

那么新的问题来了,{passive: false}是什么玩意?来,我们先看看它的使用方式:

document.addEventListener('touchmove', function(e) { e.preventDefault(); }, { passive: false });

按照以往我们对添加事件监听的方法三个参数的认知,如下:

document.addEventListener(type , callback, capture); //type是事件类型,callback是执行函数, capture是否进行捕获/冒泡,默认为false

Passive event listeners是2016年Google I/O 上同 PWA 概念一起被提出,但是同PWA不同,Passive event listeners 的作用很简单,如果用简单一句话来解释就是:提升页面滑动的流畅度。

target.addEventListener(type, listener[, options]); /**options 可选一个指定有关 listener 属性的可选参数对象。可用的选项如下:capture:  Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。once:  Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。passive: Boolean,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。*/ //示例代码target.addEventListener('touchstart', function(e){ e.preventDefault() // 无效,报错 }, { passive: true});

为什么增加支持这个属性会导致添加fastclick后input输入框很难弹出键盘?

在ios更新日志了,写到了“Updated root document touch event listeners to use passive mode improving scrolling performance and reducing crashes.”

翻译过来就是:针对document的touch事件监听添加passive配置,即是:{passive: true},会永远不调用event.preventDefault(),以此来提高滚动性能。

源头推测:

fastclick是采用拦截click和监听touch事件去实现的,里面包括对tagetElement的focus方法重写,因此在11.3之前可能event.preventDefault生效了,同时用setSelectionRange是可以聚焦input的。

另外一个bug也是由这个导致的是:

在iOS11.3的UI webview使用fastclick.js,页面有个按钮点击事件,当app或锁屏超过几分钟时间,回到页面会导致click事件失效。

解决方案为:

var passiveListener = (function checkPassiveListener() { //判断浏览器是否支持 {passive: true} var supportsPassive = false; try { var opts = Object.defineProperty({}, 'passive', { get: function() { supportsPassive = true; } }); window.addEventListener('testPassiveListener', null, opts); } catch (e) { supportsPassive = false; } return supportsPassive; }()); var activeListener = passiveListener ? { passive:false} : false; layer.addEventListener('click', this.onClick, true); layer.addEventListener('touchstart', this.onTouchStart, passiveListener); layer.addEventListener('touchmove', this.onTouchMove, passiveListener); layer.addEventListener('touchend', this.onTouchEnd, passiveListener); layer.addEventListener('touchcancel', this.onTouchCancel, passiveListener);

参考资料

转载于:https://www.cnblogs.com/chris-oil/p/10466441.html

你可能感兴趣的文章
mysql内存优化
查看>>
都市求生日记第一篇
查看>>
Java集合---HashMap源码剖析
查看>>
SQL优化技巧
查看>>
thead 固定,tbody 超出滚动(附带改变滚动条样式)
查看>>
Dijkstra算法
查看>>
css 动画 和 响应式布局和兼容性
查看>>
csrf 跨站请求伪造相关以及django的中间件
查看>>
MySQL数据类型--与MySQL零距离接触2-11MySQL自动编号
查看>>
生日小助手源码运行的步骤
查看>>
Configuration python CGI in XAMPP in win-7
查看>>
bzoj 5006(洛谷 4547) [THUWC2017]Bipartite 随机二分图——期望DP
查看>>
CF 888E Maximum Subsequence——折半搜索
查看>>
欧几里德算法(辗转相除法)
查看>>
面试题1-----SVM和LR的异同
查看>>
MFC控件的SubclassDlgItem
查看>>
如何避免历史回退到登录页面
查看>>
《图解HTTP》1~53Page Web网络基础 HTTP协议 HTTP报文内的HTTP信息
查看>>
unix环境高级编程-高级IO(2)
查看>>
树莓派是如何免疫 Meltdown 和 Spectre 漏洞的
查看>>