在小程式中,我們經常有調起鍵盤的操作場景,但是在不同的場景下解決方案不盡相同,還是需要具體問題具體分析。
需求分析
最近在專案中有一個需求,是從列表頁點選評論按鈕進入詳情頁時,在載入完頁面後自動調起鍵盤進入評論狀態。從需求來看,我們應該在onReady函式中調起鍵盤,因為onReady函式是在頁面初次渲染完成時被呼叫。但是在實踐中我們發現,對於一些配置不好的手機,其載入頁面速度較慢,在onReady函式呼叫時頁面並沒有渲染完畢,就會導致placeholder和input元件位置錯亂的現象。其本質原因是,onReady生命週期函式並不能在呼叫時承若已經將頁面渲染完成了。(儘管文件中描述是已經完成了。)
之前的操作是在onReady生命週期函式中調起鍵盤。
this.setData({ focus: true })
複製程式碼
發現這個問題後做了相應的延遲處理
setTimeout(() => {
this.setData({ focus: true })
}, 300)
複製程式碼
但這是治標不治本的方法,手機效能好的使用者會無謂的等待300毫秒,而手機效能很差的使用者等待300毫秒也不一定就能解決這個問題。
解決思路
那麼既然小程式並沒有提供給我們一個理想的渲染結束後的回撥函式,那麼我們就換個思路:使用短輪詢來處理,當頁面渲染完成後才調起鍵盤的操作。
既然要使用短輪詢,那麼我們去輪詢什麼呢?什麼標誌代表著頁面渲染完成了呢?在這裡,我是使用wx.createSelectorQuery()
方法,它會返回一個SelectorQuery物件例項,在這個例項上呼叫select方法選擇我想要去輪詢的節點,在回撥函式中判斷引數是否為null
。如果返回了監控的節點資訊,那麼說明已經渲染完成。這時就可以進行鍵盤調起操作了。
let timer = setInterval(() => {
wx.createSelectorQuery().select(`#comment-section`).boundingClientRect(rect => {
if (rect !== null && timer !== null) {
clearInterval(timer)
timer = null
this.setData({ focus: true })
}
}).exec()
}, 50)
複製程式碼
在此之上,如果我們只粗暴的讓focus
為true
並不是個明智的做法。
在調起鍵盤時預設頁面會上推,如果在評論很少的情況下這樣的體驗並不好。所以需要判斷一個高度,超過這個值就上推,沒超過就不上推。這個值視實際情況而定。
上推的操作是由input元件的adjust-position
屬性決定,為true則上推,否則則不上推。這時回撥返回的引數中的節點資訊就可以派上用場了。
// 在this.setData({ focus: true })前對節點高度進行判斷
if (rect.height < 500) this.setData({ push: false })
else this.setData({ push: true })
複製程式碼
onBlur函式問題
在實際的操作中,我們發現在鍵盤被調起後會有概又自動收回。經過排查發現時onBlur函式的問題,在onBlur函式中,我們手動的設定focus
為false
,但其實並不需要這一步操作,反而帶來了副作用。在我們去除了這部分程式碼後,鍵盤自動收起的問題得到了解決。
封裝起來
雖然我們完成了這次任務的需求,但是顯而易見的,這樣的任務在未來肯定還會再次出現。所以機智的我們應該趕快把整套流程封裝起來,以便下次直接呼叫。
那麼這時我們使用的方式就是這樣的:
const Util = require("xxx") // 引入封裝的庫
/**
* 生命週期函式--監聽頁面初次渲染完成
*/
onReady: function () {
Util.onTotalReady(`#comment-section`, 50, rect => {
if (rect.bottom < 500) this.setData({ push: false })
else this.setData({ push: true }}
this.setData({ focus: true })
})
}
複製程式碼
小結
在解決鍵盤調起的這個過程中我們可以看出微信小程式開發流程的簡陋,這個問題的出現本質上是小程式提供給我們的生命週期函式的不夠準確。否則在頁面渲染完成的情況下我怎麼會拿不到節點資訊呢?像react中的componentWillMount生命週期函式中就不會出現這樣的問題,所以希望小程式能再變強大一些,也讓我們少寫一點這種hack程式碼。