移動端input開發問題

做爲前端開發,工做中不可避免的要接觸input,要基於input作一些定製化開發,但由於安卓ios系統的差別性,宿主app的webview選擇(特指ios),app的歷史遺留問題等等,會出現大量的兼容性問題css

下面,對我開發過程當中遇到的問題以及解決方案進行記錄,但願能幫到大家html

1. ios webview類型

ios下不一樣的app內置的webview可能不一樣,致使input行爲不一致,因此咱們先大體瞭解一下,目前ios系統下有兩種webview:WK和UI前端

ios8以前,一直使用的是UI,可是從ios8開始,新增了WK類型,相比UI,WK加載速度更快,消耗內存更少,而且支持更多h5的特性,因此各大公司基本都會對此進行升級,但不排除有些app由於各類歷史緣由還在使用UI,好比咱們,目前咱們的h5頁面運行在多個app內,有UI也有WK,天然在開發的時候作一些兼容判斷ios

判斷webview類型git

// 暫沒發現好的方法來判斷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'
}

2. autofocus失效

不論是h5規範仍是在caniuse上面查,這個屬性都是支持的,但你在真機上面測試的時候,不少狀況下,並非你想要的效果github

目前我測試狀況以下:web

  • iosapi

    • UIWebview,能夠聚焦,有光標,能呼起鍵盤
    • WKWebview,能夠聚焦,有光標,<u>但呼不起鍵盤</u>
  • 安卓瀏覽器

    • 暫時沒發現問題(但不絕對)

網上了解到的緣由是,蘋果但願由用戶來觸發,輸入這個操做是用戶的意願,而不是強制的,因此在新版本系統中禁止了這個屬性,老版本的UI上面尚未這個限制app

但慶幸的是,不論是安卓仍是ios,都支持在touch或者click事件中<u>同步</u>的執行input.focus()來聚焦並呼起鍵盤,

若是業務場景符合這種狀況,那能夠經過這種方式來作

onClick = () => {
  this.input.focus()
}

但若是在點擊事件中有異步操做時,同autofocus屬性,只能聚焦,不能呼起鍵盤

onClick = () => {
  setTimeout(() => {
    this.input.focus()
  }, 1000)
  
  // 或者
fetch('/api/get').then(() => {
    this.input.focus()
  })
}

降級處理

若是業務場景不符合上面的狀況,但又非要這個功能,有一種方案是,聚焦以後,強化一下聚焦的效果(由於原生的光標展現畢竟不明顯)

  • 設置一個聚焦的樣式,在input上也好,在頁面其餘元素也好,由本身產品特點以及UE決定
  • 適當的滾動頁面,使其到視圖中間

可參考

3. 聚焦問題

3.1 點擊沒法聚焦或者聚焦以後又失焦

大部分狀況是移動端300ms問題引發的

一種是:app使用UIWebview可是h5頁面沒引入fastclick

另外一種是:引入了fastclick,但須要對fastclick的focus進行優化,改成

FastClick.prototype.focus = function (targetElement) {
    targetElement.focus();
};

3.2 光標沒法聚焦到點擊位置

這個大機率也是fastclick致使的,可參考:連接

3.3 已有聚焦input的狀況下點擊其餘input,會跳動

這個大機率也是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
}

3.4 建議

上面這幾個問題,大部分是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.

4. 失焦問題

4.1 但願保持聚焦

有時候,但願點擊頁面其餘地方的時候,input保持聚焦狀態,但瀏覽器默認行爲是將input失焦

解決方案:在點擊事件中,阻止默認行爲

function onClick(e) {
    // 你的事件處理代碼
    ...
    
    e.preventDefault();
    // iphone有的機型下,沒有阻止掉默認行爲,主動再聚焦一下
    input.focus();
}

4.2 但願主動失焦

安卓某些機型有如下問題

  • 聚焦狀況下,點擊頁面其餘地方,不主動失焦
  • 某些狀況下,主動隱藏鍵盤以後,input並無失焦,可是用戶觸摸頁面其餘地方時,由於input還處於聚焦狀態,會呼起鍵盤

針對性的解決方案也分兩種

第一種:監聽用戶行爲,主動失焦

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)

5. 聚焦以後滾動到視圖內

大部分狀況下,系統會幫你將input滾動到視圖內,少數狀況下,須要咱們本身設置

  • 一種方案是,直接調用scrollIntoViewIfNeeded api
  • 另外一種是,計算聚焦先後文檔高度的變化差值,而後將body高度相應的調大,而且設置scrollTop,<u>這種狀況應該是出如今UIWebview下</u>
// 正常處理
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

6. 強制光標到末尾

input.addEventListener('touchend', e => {
  const length = e.target.value.length
  e.target.setSelectionRange(length, length);
  e.preventDefault()
  e.target.focus()
})

7. 其餘的一些問題

7.1 光標隱藏

某些場景下,須要自定義輸入框,自定義光標等

安卓能夠經過opacity: 0; color: transparent等將原生光標隱藏

但ios沒法隱藏,只能將input框移動到視圖外,先將input寬度設置足夠大,而後左移:

input {
  width: 1000px;
  margin-left: -200px;
}

7.2 複製粘貼

安卓和ios都支持長按複製粘貼,但ios的前提是,光標必須在視圖內,像上面那樣將input左移以後,粘貼按鈕不在視圖內,沒法粘貼

7.3 短信驗證碼(或其餘數字)展現在系統鍵盤

新版ios(應該是12+,不肯定)系統收到短信之後會直接將短信展現在鍵盤上面,用戶一點就能夠輸入到input,但老版不支持,須要用戶主動去短信複製,而後再粘貼到input,若是input是自定義的,須要注意上面問題

部分安卓機器(好比小米6+)收到短信,可直接點通知欄進行復制,一樣會出如今鍵盤上,其餘機型須要用戶長按粘貼

8. 結語

上面基本是我開發過程當中遇到的問題了,隨着系統的迭代升級,不少問題會獲得官方的解決,可是咱們沒法決定app是否升級,沒法決定用戶是否升級

因此,若是大家也遇到這些問題,但願能夠給予大家啓發,幫助

相關文章
相關標籤/搜索