image.png
問題描述:小米機在軟鍵盤彈起時輸入框被遮擋了
發現問題后打算使用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()
}
好了,圓滿解決,開心
在簡書上發布相關文章是對自己不斷學習的激勵;如有什么寫得不對的地方,歡迎批評指正;給我點贊的都是小可愛 ~_~