1. 程式人生 > >iOS 上,fixed 元素內的輸入元素,獲取焦點時的游標錯位問題

iOS 上,fixed 元素內的輸入元素,獲取焦點時的游標錯位問題

RT,如果一個輸入元素(input, textarea …)的父容器設定了 position: fixed,當這個元素獲取焦點時,會觸發底部鍵盤的彈起。這時在輸入框內打字的時候,會發現其游標錯位了,一般會跑到下方。


demo

當你專注於一個輸入時,瀏覽器會自動向下滾動,以便將焦點輸入突出顯示給使用者,這就造成了頁面內高度浮動,導致游標位移。

遺憾的是,截至目前,iOS 11.x 上也有這個問題。

曾經嘗試過的方案

當元素獲取焦點時,改變父容器的定位方式:fixed > absolute

for (let evt of ['focus', 'blur']) {
  const isFocus = evt === 'focus'
  const fn = isFocus ? 'add' : 'remove'

  inputDOMNode.addEventListener(evt, () => {
    parentDOMNode.classList[fn]('input-focus')
    htmlDOMNode.classList[fn]('no-scroll')
    bodyDOMNode.classList[fn]('no-scroll')

    isFocus && setScrollTop(0)
  })
}
.input-focus {
  position: absolute;
  ...
}

.no-scroll {
  height: 100%;
  overflow: hidden;
}

監聽了輸入元素 focus 和 blur 事件,為父元素新增或移除某些樣式。

當 position: absolute 時,輸入框的定位方式需要手動設定(這裡採取了頂部對齊);.no-scroll 是為了禁止 body 的滾動,保證輸入框可見。

但是這個方案在部分 Android 裝置上,當鍵盤收起時並不會觸發輸入元素的 blur 事件,往往還需要使用者主動點選頁面的其他區域,算是一點小遺憾吧。

終極解決方案(推薦)

直接給 htmlbody 元素設定樣式

html,
body {
  -webkit-overflow-scroll: touch !important;
  overflow: auto !important;
  height: 100% !important;
}
-webkit-overflow-scrolling: touch; /* 當手指從觸控式螢幕上移開,會保持一段時間的滾動 */
overflow: auto; /* 由瀏覽器定奪,如果內容被修剪,就會顯示滾動條 */

當輸入元素獲取焦點時,鍵盤彈起,輸入元素被頂到了鍵盤的上方,此時使用者的手指會從觸控式螢幕上移開,輸入元素會保持一段時間的滾動,從而游標的位置可以被正確計算。

!important 在這裡是為了防止這些屬性會因為瀏覽器優先順序過高而發生變化。
有點小遺憾的是,!important 侵入性有些高。