做爲前端開發,工做中不可避免的要接觸input,要基於input作一些定製化開發,但由於安卓ios系統的差別性,宿主app的webview選擇(特指ios),app的歷史遺留問題等等,會出現大量的兼容性問題css
下面,對我開發過程當中遇到的問題以及解決方案進行記錄,但願能幫到大家html
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' }
不論是h5規範仍是在caniuse上面查,這個屬性都是支持的,但你在真機上面測試的時候,不少狀況下,並非你想要的效果github
目前我測試狀況以下:web
iosapi
安卓瀏覽器
網上了解到的緣由是,蘋果但願由用戶來觸發,輸入這個操做是用戶的意願,而不是強制的,因此在新版本系統中禁止了這個屬性,老版本的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() }) }
若是業務場景不符合上面的狀況,但又非要這個功能,有一種方案是,聚焦以後,強化一下聚焦的效果(由於原生的光標展現畢竟不明顯)
大部分狀況是移動端300ms問題引發的
一種是:app使用UIWebview可是h5頁面沒引入fastclick
另外一種是:引入了fastclick,但須要對fastclick的focus進行優化,改成
FastClick.prototype.focus = function (targetElement) { targetElement.focus(); };
這個大機率也是fastclick致使的,可參考:連接
這個大機率也是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 }
上面這幾個問題,大部分是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.
有時候,但願點擊頁面其餘地方的時候,input保持聚焦狀態,但瀏覽器默認行爲是將input失焦
解決方案:在點擊事件中,阻止默認行爲
function onClick(e) { // 你的事件處理代碼 ... e.preventDefault(); // iphone有的機型下,沒有阻止掉默認行爲,主動再聚焦一下 input.focus(); }
安卓某些機型有如下問題
針對性的解決方案也分兩種
第一種:監聽用戶行爲,主動失焦
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)
大部分狀況下,系統會幫你將input滾動到視圖內,少數狀況下,須要咱們本身設置
// 正常處理 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
input.addEventListener('touchend', e => { const length = e.target.value.length e.target.setSelectionRange(length, length); e.preventDefault() e.target.focus() })
某些場景下,須要自定義輸入框,自定義光標等
安卓能夠經過opacity: 0; color: transparent等將原生光標隱藏
但ios沒法隱藏,只能將input框移動到視圖外,先將input寬度設置足夠大,而後左移:
input { width: 1000px; margin-left: -200px; }
安卓和ios都支持長按複製粘貼,但ios的前提是,光標必須在視圖內,像上面那樣將input左移以後,粘貼按鈕不在視圖內,沒法粘貼
新版ios(應該是12+,不肯定)系統收到短信之後會直接將短信展現在鍵盤上面,用戶一點就能夠輸入到input,但老版不支持,須要用戶主動去短信複製,而後再粘貼到input,若是input是自定義的,須要注意上面問題
部分安卓機器(好比小米6+)收到短信,可直接點通知欄進行復制,一樣會出如今鍵盤上,其餘機型須要用戶長按粘貼
上面基本是我開發過程當中遇到的問題了,隨着系統的迭代升級,不少問題會獲得官方的解決,可是咱們沒法決定app是否升級,沒法決定用戶是否升級
因此,若是大家也遇到這些問題,但願能夠給予大家啓發,幫助