问题描述:小米机在软键盘弹起时输入框被遮挡了
发现问题后打算使用scrollIntoView来处理这个兼容问题,即给表单添加onClick事件,在触发时调用scrollIntoView方法。
<textarea
name="备注"
onClick={e => {
console.log(document.body.clientHeight);//667
e.target.scrollIntoView({
behavior: 'smooth'
})
}}
/>
但是,问题并没有解决,为什么呢?
有个坑:
- onclick触发之后,viewpoint的改变还需要时间,此时立刻执行的话,viewpoint还是667,并不是软键盘弹出后的视窗大小
如何解决呢?
加个setTimeout好了
<textarea
name="备注"
onClick={e => {
setTimeout(() => {
console.log(document.body.clientHeight);//371
console.log(e.target);//null
e.target.scrollIntoView({
behavior: 'smooth'
})
}, 300)
}}
/>
此时,viewpoint的问题是解决了,但是发现还是不生效,打印出e.target一看,瞬间很凌乱,null?明明在外层是整整齐齐的textarea对象啊。
这是因为React 的SyntheticEvent导致的
React的SyntheticEvent是共享的。那就意味着在调用事件回调之后,SyntheticEvent对象将会被重用,并且所有属性会被置空。这是出于性能因素考虑的。 因此,你无法以异步方式访问事件。
官网也提供了解决方案:
如果要以异步方式访问事件属性,应该对事件调用 event.persist() ,这将从池中删除合成事件,并允许用户代码保留对事件的引用。
当然,我们也可以在setTimeout外面暂存下e.target变量,同样也能解决这个问题。
优化
如果需要使用的地方有点多,我们还可以封装成方法,并且在外层元素上使用事件监听。
//封装在xxx.js里的方法
export function on(docElemenet, eventName, func) {
docElemenet.addEventListener(eventName, func)
return () => {
docElemenet.removeEventListener(eventName, func)
}
}
export function scrollIntoView(elem) {
setTimeout(() => {
elem.scrollIntoView({
behavior: 'smooth'
})
}, 300)
}
//写在组件中
componentDidMount() {
this.removeListener = on(this.ref.current, 'click', e => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
scrollIntoView(e.target)
}
})
}
componentWillUnmount() {
this.removeListener && this.removeListener()
}
好了,圆满解决,开心
在简书上发布相关文章是对自己不断学习的激励;如有什么写得不对的地方,欢迎批评指正;给我点赞的都是小可爱 ~_~