最近遇到奇异的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);

参考资料

[转] iOS11.3 fastclick.js相关bug的更多相关文章

  1. fastclick使用与 fastclick ios11.3相关bug原因(ios输入框点击变得不灵敏,ios input失焦后,页面上移,点击不了)

    FastClick 移动设备上的浏览器默认会在用户点击屏幕大约延迟300毫秒后才会触发点击事件,这是为了检查用户是否在做双击.为了能够立即响应用户的点击事件,就有了FastClick. 安装fastc ...

  2. fastclick.js源码解读分析

    阅读优秀的js插件和库源码,可以加深我们对web开发的理解和提高js能力,本人能力有限,只能粗略读懂一些小型插件,这里带来对fastclick源码的解读,望各位大神不吝指教~! fastclick诞生 ...

  3. IOS中div contenteditable=true无法输入 fastclick.js在点击一个可输入的div时,ios无法正常唤起输入法键盘

    原文地址: https://blog.csdn.net/u010377383/article/details/79838562 前言 为了提升移动端click的响应速度,使用了fastclick.js ...

  4. 引用fastclick.js或使用触屏监听 滑动屏幕报错:解决[Intervention] Unable to preventDefault inside passive event listener

    使用fastClick.js所产生的一些问题 开发h5活动页时想到移动端会有300ms的延迟,于是便打算用fastClick.js解决. 页面引入fastClick.js后,滑动H5页面的时候发现谷歌 ...

  5. 使用fastClick.js所产生的一些问题

    开发h5活动页时想到移动端会有300ms的延迟,于是便打算用fastClick.js解决. 页面引入fastClick.js后,滑动H5页面的时候发现谷歌浏览器会报错,如下: Unable to pr ...

  6. SQL Server 优化器特性导致的内存授予相关BUG

    我们有时会遇到一些坑,要不填平,要不绕过.这里为大家介绍一个相关SQL Server优化器方面的特性导致内存授予的相关BUG,及相关解决方式,也顺便回答下邹建同学的相关疑问. 问题描述 一个简单的查询 ...

  7. 关于fastclick.js

    Fastclick fastclick.js解决了什么问题? 自己接触WebApp开发的前期, 总感觉WebApp上的按键操作不如NativeApp的灵敏, 好像有那么一小点延迟. 后来才知道, 这是 ...

  8. fastclick.js介绍

    原文地址:http://www.uedsc.com/fastclick.html 用途:去掉移动端click事件的300ms的延迟. 延迟为什么存在   …在移动浏览器中,当你点击按钮的单击事件时,将 ...

  9. Js相关用法个人总结

    Js相关用法个人总结  js中将数组元素添加到对象中var obj = {}; var pushArr = [11,22,33,44,55,66]; for(var i=0;i<pushArr. ...

随机推荐

  1. 【LOJ#3097】[SNOI2019]通信(费用流)

    [LOJ#3097][SNOI2019]通信(费用流) 题面 LOJ 题解 暴力就直接连\(O(n^2)\)条边. 然后分治/主席树优化连边就行了. 抄zsy代码,zsy代码是真的短 #include ...

  2. centos7下使用docker安装gitlab

    环境背景: Docker化已经成为一种热门,记录一下使用docker引擎安装gitlab的过程. 测试环境: 系统 软件 依赖 CentOS 7.4 GitLab(latest) docker-ce ...

  3. MySQL8.0.19主从环境搭建(CentOS7)

    默认情况下,复制是异步的,从站不需要永久连接以接收来自主站的更新.根据配置,您可以复制数据库中的所有数据库,所选数据库甚至选定的表. MySQL中复制的优点包括: 横向扩展解决方案 - 在多个从站之间 ...

  4. python计算素数和

    计算输入两个正整数x,y(x<=y,包括x,y)素数和.函数isPrime用以判断一个数是否素数,primeSum函数返回素数和 以下为源码 def isPrime(n) :    for i ...

  5. Lending Club贷款数据分析

    python信用评分卡(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_camp ...

  6. 编写高质量的Python代码系列(六)之内置模块

    Python预装了许多写程序时会用到的重要模块.这些标准软件包与通常意义上的Python语言联系得非常精密,我们可以将其当成语言规范的一部分.本节将会讲解基本的内置模块. 第四十二条:用functoo ...

  7. Java面试题[转载]

    目录 转载 简历篇 请自我介绍 请介绍项目 基础篇 基本功 面向对象的特征 final, finally, finalize 的区别 int 和 Integer 有什么区别 重载和重写的区别 抽象类和 ...

  8. linux搭建FastDFS文件服务器

    本文主要介绍在linux服务器如何搭建FastDFS文件服务器.大概分为9个步骤,由于内容较为繁琐.下面带你入坑! 首先简单介绍一下FastDFS是淘宝资深架构师余庆老师主导开源的一个分布式文件系统, ...

  9. ultraEdit软件比较两个文件内容的不同处

    1.软件名称为:UltraEdit ,安装并打开软件; 软件图标: 打开软件如图所示: 2.点击导航图标,蓝色上面有Uc图标,该图标名称为“比较文件” 如图位置: 3.弹出框,根据文件路径选择好比较的 ...

  10. Learn Node.js

    Learn Node.js Node: 脱离浏览器运行的JS,运行在服务端 基于Chrome浏览器的V8引擎,使用V8虚拟机解析和执行JS代码 创建简单的服务器: 创建一个server.js的文件 $ ...