做为前端开发,工做中不可避免的要接触input,要基于input作一些定制化开发,但由于安卓ios系统的差别性,宿主app的webview选择(特指ios),app的历史遗留问题等等,会出现大量的兼容性问题css
下面,对我开发过程当中遇到的问题以及解决方案进行记录,但愿能帮到大家html
ios下不一样的app内置的webview可能不一样,致使input行为不一致,因此咱们先大体了解一下,目前ios系统下有两种webview:WK和UI前端
ios8以前,一直使用的是UI,可是从ios8开始,新增了WK类型,相比UI,WK加载速度更快,消耗内存更少,而且支持更多h5的特性,因此各大公司基本都会对此进行升级,但不排除有些app由于各类历史缘由还在使用UI,好比咱们,目前咱们的h5页面运行在多个app内,有UI也有WK,天然在开发的时候作一些兼容判断ios
判断webview类型git
// 暂没发现好的方法来判断webview的类型,除非ios开发告诉你 // 下面这个方式是经过某些只有wk支持的h5新特性来判断 function getIosWebviewType() { if (navigator.platform.substr(0, 2) === 'iP') { // iOS (iPhone, iPod or iPad) const lte9 = /constructor/i.test(window.HTMLElement); const nav = window.navigator; const ua = nav.userAgent; const idb = !!window.indexedDB; if (ua.indexOf('Safari') !== -1 && ua.indexOf('Version') !== -1 && !nav.standalone) { return 'Safari' } else if ((!idb && lte9) || !window.statusbar.visible) { return 'UI' } else if ((window.webkit && window.webkit.messageHandlers) || !lte9 || idb) { return 'WK' } } return 'unknown' }
不论是h5规范仍是在caniuse上面查,这个属性都是支持的,但你在真机上面测试的时候,不少状况下,并非你想要的效果github
目前我测试状况以下:web
iosapi
安卓浏览器
网上了解到的缘由是,苹果但愿由用户来触发,输入这个操做是用户的意愿,而不是强制的,因此在新版本系统中禁止了这个属性,老版本的UI上面尚未这个限制app
但庆幸的是,不论是安卓仍是ios,都支持在touch或者click事件中<u>同步</u>的执行input.focus()来聚焦并呼起键盘,
若是业务场景符合这种状况,那能够经过这种方式来作
onClick = () => { this.input.focus() }
但若是在点击事件中有异步操做时,同autofocus属性,只能聚焦,不能呼起键盘
onClick = () => { setTimeout(() => { this.input.focus() }, 1000) // 或者 fetch('/api/get').then(() => { this.input.focus() }) }
若是业务场景不符合上面的状况,但又非要这个功能,有一种方案是,聚焦以后,强化一下聚焦的效果(由于原生的光标展现毕竟不明显)
大部分状况是移动端300ms问题引发的
一种是:app使用UIWebview可是h5页面没引入fastclick
另外一种是:引入了fastclick,但须要对fastclick的focus进行优化,改成
FastClick.prototype.focus = function (targetElement) { targetElement.focus(); };
这个大几率也是fastclick致使的,可参考:连接
这个大几率也是fastclick致使,可参考上面那个连接
或者这样改fastclick源码,在onTouchEnd中新增判断,是否须要走原生聚焦逻辑
FastClick.prototype.onTouchEnd = function (event) { // ... if (targetTagName === 'label') { // ... } else if (this.needsFocus(targetElement)) { // 新增 if (!this.needsFocusInput()) return false // ... } // ... }; // 新增 // 已有聚焦元素的状况下,直接走原生input聚焦逻辑 FastClick.prototype.needsFocusInput = function () { const focusInput = document.querySelector('input:focus') return !focusInput }
上面这几个问题,大部分是fastclick致使的,但fastclick所解决的问题(300ms),目前大部分浏览器已经解决了,因此确认一下,若是大家的app不用UIWebview的话,那能够直接去掉fastclick了
fastclick github说明:
Note: As of late 2015 most mobile browsers - notably Chrome and Safari - no longer have a 300ms touch delay, so fastclick offers no benefit on newer browsers, and risks introducing bugs into your application. Consider carefully whether you really need to use it.
有时候,但愿点击页面其余地方的时候,input保持聚焦状态,但浏览器默认行为是将input失焦
解决方案:在点击事件中,阻止默认行为
function onClick(e) { // 你的事件处理代码 ... e.preventDefault(); // iphone有的机型下,没有阻止掉默认行为,主动再聚焦一下 input.focus(); }
安卓某些机型有如下问题
针对性的解决方案也分两种
第一种:监听用户行为,主动失焦
const autoBlur = (e) => { const target = e.target const { tagName, className } = target // 点击非input区域 if (tagName.toUpperCase() !== 'INPUT') { this.input.blur() } } document.body.addEventListener('touchstart', autoBlur)
第二种:监听键盘高度变化,主动失焦
const onKeyboardChange = (resize) => { // 有时候,好比number变成text,或者系统自动在键盘上面加一些装饰,键盘并无隐藏,可是触发了resize // 测试大部分机型,全部的键盘确定大于120高度了,因此加一个限制 if (Math.abs(resize) < 120) return const show = resize > 0 if (!show) { this.input.blur() } } function getClientHeight() { return document.documentElement.clientHeight || document.body.clientHeight; } // 记录原始高度 let originHeight = getClientHeight() // 监听键盘变化 window.addEventListener('resize', () => { const resizeHeight = getClientHeight() const resize = originHeight - resizeHeight onKeyboardChange(resize); originHeight = resizeHeight; }, false)
大部分状况下,系统会帮你将input滚动到视图内,少数状况下,须要咱们本身设置
// 正常处理 input.addEventListener('focus', () => { setTimeout(() => { this.input.scrollIntoViewIfNeeded(); }, 300) })
// UIWebview下的处理 function getClientHeight() { return document.documentElement.clientHeight || document.body.clientHeight; } const bodyHeight = getClientHeight() const bodyOverflow = getComputedStyle(document.body).overflow input.addEventListener('focus', () => { document.body.style.overflow = 'auto' setTimeout(() => { // alert(getClientHeight()) let height = bodyHeight - getClientHeight() if (height < 0) height = height * -2 document.body.style.height = `${bodyHeight + height}px`; document.body.scrollTop = height; }, 300) }) // 若是设置了高度,在blur时要设置回来 input.addEventListener('blur', () => { document.body.style.height = `${bodyHeight}px`; document.body.style.overflow = bodyOverflow })
加setTimeout是由于唤起键盘会有一个动画,咱们须要在动画结束以后再滚动页面,不然计算出的高度不许大部分状况下,键盘动画时间小于300
input.addEventListener('touchend', e => { const length = e.target.value.length e.target.setSelectionRange(length, length); e.preventDefault() e.target.focus() })
某些场景下,须要自定义输入框,自定义光标等
安卓能够经过opacity: 0; color: transparent等将原生光标隐藏
但ios没法隐藏,只能将input框移动到视图外,先将input宽度设置足够大,而后左移:
input { width: 1000px; margin-left: -200px; }
安卓和ios都支持长按复制粘贴,但ios的前提是,光标必须在视图内,像上面那样将input左移以后,粘贴按钮不在视图内,没法粘贴
新版ios(应该是12+,不肯定)系统收到短信之后会直接将短信展现在键盘上面,用户一点就能够输入到input,但老版不支持,须要用户主动去短信复制,而后再粘贴到input,若是input是自定义的,须要注意上面问题
部分安卓机器(好比小米6+)收到短信,可直接点通知栏进行复制,一样会出如今键盘上,其余机型须要用户长按粘贴
上面基本是我开发过程当中遇到的问题了,随着系统的迭代升级,不少问题会获得官方的解决,可是咱们没法决定app是否升级,没法决定用户是否升级
因此,若是大家也遇到这些问题,但愿能够给予大家启发,帮助