大家好,我是编程小6,很高兴遇见你,有问题可以及时留言哦。
作为前端开发,工作中不可避免的要接触input,要基于input做一些定制化开发,但因为安卓ios系统的差异性,宿主app的webview选择(特指ios),app的历史遗留问题等等,会出现大量的兼容性问题
下面,对我开发过程中遇到的问题以及解决方案进行记录,希望能帮到你们
ios下不同的app内置的webview可能不同,导致input行为不一致,所以我们先大致了解一下,目前ios系统下有两种webview:WK和UI
ios8之前,一直使用的是UI,但是从ios8开始,新增了WK类型,相比UI,WK加载速度更快,消耗内存更少,并且支持更多h5的特性,所以各大公司基本都会对此进行升级,但不排除有些app因为各种历史原因还在使用UI,比如我们,目前我们的h5页面运行在多个app内,有UI也有WK,自然在开发的时候做一些兼容判断
判断webview类型
// 暂没发现好的方法来判断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上面查,这个属性都是支持的,但你在真机上面测试的时候,很多情况下,并不是你想要的效果
目前我测试情况如下:
网上了解到的原因是,苹果希望由用户来触发,输入这个操作是用户的意愿,而不是强制的,所以在新版本系统中禁止了这个属性,老版本的UI上面还没有这个限制
但庆幸的是,不管是安卓还是ios,都支持在touch或者click事件中同步的执行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滚动到视图内,少数情况下,需要我们自己设置
一种方案是,直接调用scrollIntoViewIfNeeded api
另一种是,计算聚焦前后文档高度的变化差值,然后将body高度相应的调大,并且设置scrollTop,这种情况应该是出现在UIWebview下
// 正常处理
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是否升级,无法决定用户是否升级
所以,如果你们也遇到这些问题,希望可以给予你们启发,帮助
上一篇
已是最后文章