Chrome: 61.0.3163.73javascript
Safari: 10.0(IOS 10.3.3)css
Github: webapp-bugshtml
滑動到最頂部(最底部)的時候,停下,而後繼續向上滑動(向下滑動)java
scrollTop
(scrollFix)當快滑到上邊界或者下邊界的值時,手動設置scrollTop
與達到邊界時相差一像素(上邊界時:scrollTop = 1
, 下邊界時:scrollTop = elem.scrollHeight - elem.offsetHeight - 1
),這樣就不會觸發出界的極限條件。node
具體實現:jquery
var ScrollFix = function(elem) { // Variables to track inputs var startY, startTopScroll; elem = elem || document.querySelector(elem); // If there is no element, then do nothing if(!elem) return; // Handle the start of interactions elem.addEventListener('touchstart', function(event){ startY = event.touches[0].pageY; startTopScroll = elem.scrollTop; if(startTopScroll <= 0) elem.scrollTop = 1; if(startTopScroll + elem.offsetHeight >= elem.scrollHeight) elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1; }, false); };
注:1. 這個方法只能部分防止,在某些時候仍是會觸發出界。2. 有說在全局滾動下和局部滾動下會有差別,可是就我測試的狀況來講,差別並非特別大。多是版本過高的緣由,具體結論還待測試更多機型。android
static
變爲fixed
(測試效果貌似更好).toolbar { -webkit-box-sizing: border-box; padding: 1em; background: #222; color: #fff; font-weight: bold; text-align: center; height: 50px; /* 添加fixed頭部 */ position: fixed; top: 0; z-index: 1; width: 100%; }
看以下代碼:ios
// html <input type="email" class="form-control" id="inputEmail3" placeholder="Email"> <button id="submitBtn" class="btn btn-default">Sign in</button> // script var inputEmail3 = document.querySelector('#inputEmail3'); var submitBtn = document.querySelector('#submitBtn'); // way1 setTimeout(() => { inputEmail3.focus(); }, 2000);
這種方式下:在IOS
上輸入框聚焦確沒有辦法彈出鍵盤git
爬牆爬到這麼一個issue,3樓eddiemonge
老哥說到了,在IOS
下除非用戶手動觸發了輸入框的focus
事件,纔會觸發鍵盤,至於設置定時器也是無論用的;可是,手動點擊一個按鈕,在按鈕的操做中再來執行focus
事件卻是管用的。他還給出了一個http://jsbin.com/inunis/8/edit?html,js,outputgithub
// 這樣是能夠彈出鍵盤的 submitBtn.onclick = function(e) { e.preventDefault(); inputEmail3.focus(); }
我爲何會關注這個問題:那是由於我**(這裏省略一萬個草泥馬)也遇到了這個問題呀,容我細細說來。
我有一個登陸頁面,在聚焦以後須要往上彈一下,android
上正常,而後IOS
上還同時引出了一個BUG
:輸入框上去了,可是光標卻在下面閃。怎麼辦呢?固然是靠想辦法解決呀,後來我就想在輸入框上貼一層蒙版,點擊了以後消失,同時在點擊操做中,等到動畫結束以後再執行輸入框的focus
,行不行呢?好期待。。。
html
代碼是這樣的:
// ... 這裏省略若干 <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail3" placeholder="Email" /> <div class="input-overlay" id="overlay"></div> </div>
樣式:
.input-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 0, 0, 0.3); z-index: 2; }
腳本:
overlay.addEventListener('touchstart', function(e) { e.preventDefault(); testForm.style.transform = 'translate3d(0, 0, 0)'; overlay.style.display = 'none'; overlay.style.zIndex = -1; setTimeout(function() { inputEmail3.focus(); }, 200); });
正所謂想象是好樣的,可是實際行使起來卻不是那麼使人滿意。是的,毫無效果。後來我想,是否是能夠模擬一個事件,再觸發一次點擊,而後代碼是這樣的:
function mockEvent(fn) { var createDiv = document.createElement('div'); createDiv.style.display = 'none'; document.body.appendChild(createDiv); // 未作兼容 var e = document.createEvent('MouseEvent'); e.initEvent('xx', true, true); createDiv.addEventListener('xx', function() { fn && fn(); createDiv.remove(); }); createDiv.dispatchEvent(e); } overlay.addEventListener('click', function(e) { // ... setTimeout(function() { mockEvent(function() { inputEmail3.focus(); }); }, 200); });
答案依然是:不行。而後我想,是否是setTimeout
的緣由,只要存在延遲的狀況下就不行。結果我去這麼試了一下,將以前的按鈕直接點擊方式改成200ms
以後執行focus
。
submitBtn.onclick = function(e) { e.preventDefault(); setTimeout(function() { inputEmail3.focus(); }, 200); }
果真,只要設置延遲就不起效果了。頓時忽然想到移動端點透事件貌似有個300ms
延遲執行。雖然點透事件在移動端會被處理掉,然而我只是想驗證一下個人猜測。而後我又這麼寫:
// html <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail3" placeholder="Email" /> <div class="input-overlay" id="overlay"></div> <a class="input-link" href="javascript:;" id="link">link</a> </div>
在overlay
下面放一個link
,而後在overlay
上綁定touchstart
事件,在link
上綁定click
事件。這樣在上層的遮罩去掉以後,就能夠300ms
後執行下面的link
層中的事情,那麼也算是用戶真正地觸發的點擊行爲,美滋滋。結果我在代碼中加了這個東西:
overlay.addEventListener('touchstart', function(e) { // ... }); link.addEventListener('click', function() { link.style.display = 'none'; link.style.zIndex = -1; inputEmail3.focus(); });
尼瑪呀,仍是不行,絕望了。
然而。。。
天生不死心,又去爬牆呀。輸入inupt move while cursor to stay where it was
。下面講解決方案。
我找到了這樣的一個issue。在其中的描述是:他的內容中有一輸入框,而後focus
,當滑動內容時,光標不跟隨移動,而在此輸入的時候,光標又會回到輸入框中。狀況應該和我相似。
robby says
I also have this problem.
It is apparently related to the use of css transforms.I have fixed it with this hack workaround that forces redrawing as you scroll to eliminte this issue:
CSS:input { text-shadow: rgba(0,0,0,0) 0px 0px 0px; } input.force-redraw { text-shadow: none; }JS:
myScroll = new iScroll('wrapper', { onScrollMove: function() { $('input').toggleClass('force-redraw'); } });
是的,有木有很激動。因而我這樣寫:
// css input { text-shadow: rgba(0,0,0,0) 0px 0px 0px; } input.force-redraw { text-shadow: none; } // javascript inputEmail3.addEventListener('focus', function() { testForm.style.transform = 'translate3d(0, 0, 0)'; setTimeout(() => { inputEmail3.className = 'form-control force-redraw'; }, 300); });
效果大致上實現了,可是仍然有瑕疵。就是必須設置延遲300ms
以上,否則,光標重繪不正常,並且光標有明顯的移動過程。因此若是童鞋們若是發現有什麼更好的辦法,還望不吝賜教。
另外,若是一個頁面中有輸入框,聚焦以後,滑動過程當中在IOS
上可能會出現不流暢的問題,其實能夠這麼作:監測頁面的touchmove
事件,若是當前頁面存在着輸入框被active
,那麼直接讓其blur
,保證滑動過程當中沒有輸入框被聚焦。(不過以個人測試狀況來看,在chrome
和safari
上滑動的時候輸入框再也不被激活,相似在PC
端滑動的時候採用了蒙版或者points-event: none;
的效果)
var thisFocus; var content = document.querySelector('#content'); var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; input.onfocus = focused; } function focused(e) { thisFocus = e.target; } content.addEventListener('touchmove', function() { if (thisFocus) { thisFocus.blur(); thisFocus = null; } });
頁面中有fixed
頭部,輸入框,而且輸入框靠下時,當輸入框focus
的時候,會將整個頁面上移,致使頭部被頂出去。fixed position div freezes on page
緣由大體是:ios
自帶的輸入居中效果,而帶有fixed
頭部在頁面被頂上去的同時沒有從新計算位置,致使顯示錯誤。那麼能夠具體分這幾步來解決:
focus
的時候採用fixed
固定頭部focus
時,採用絕對定位頭部,同時使用window.pageYOffset
來計算滑動的距離,設置頭部的top
值scroll
方法,動態設置頭部top
值fixed
定位代碼請往這裏看:
var isFocused, focusedResizing, ticking = false; window.tryfix = function() { var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { input = inputs[i]; input.onfocus = focused; input.onblur = blured; } window.onscroll = onScroll; } function focused(event) { isFocused = true; scrolled(); } function blured(event) { isFocused = false; var headStyle = document.getElementById('header').style; var footStyle = document.getElementById('footer').style; if (focusedResizing) { focusedResizing = false; headStyle.position = 'fixed'; headStyle.top = 0; footStyle.display = 'block'; } } function onScroll() { if (!ticking) { requestAnimationFrame(scrolled); ticking = true; } } function scrolled() { var headStyle = document.getElementById('header').style; var foot = document.getElementById('footer'); var footStyle = foot.style; if (isFocused) { if (!focusedResizing) { focusedResizing = true; headStyle.position = 'absolute'; footStyle.display = 'none'; } headStyle.top = window.pageYOffset + 'px'; // window.innerHeight wrong //var footTop = window.pageYOffset + window.innerHeight - foot.offsetHeight; //footStyle.bottom = (document.body.offsetHeight - footTop) + 'px'; } ticking = false; } tryfix();
另外若是頁面縮放,也會引發頭部定位不正常。詳情能夠看這裏,關於anroid
上fixed
的支持狀況,能夠看這裏
當輸入框比較靠下時,android
上彈出鍵盤,會將輸入框遮住。
說明:測試了不少機型,發現如今的android
上的瀏覽器都貌似修復了這個問題,就是當鍵盤彈上來的時候,會默認地將輸入框上移。可是我在項目中內嵌的webview
中確實遇到了這種問題。
測試說明:測試的機型包括瞭如今一些主要的瀏覽器:chrome
、UC
、QQ
、Opera
、360
、百度、獵豹,測試的android
版本包括4.一、4.四、5.1等,測試的瀏覽器版本都有下載最低的歷史版原本測。可是就測試的狀況來看,除了獵豹瀏覽器會出現上述的狀況以外,其餘的基本表現正常。(更多測試量沒作,沒有這麼多機器呀。尷尬😓)
逗比時刻:我爲了測試較老的Android
版本,特意裝了genymotion
,後來發現根本就沒有鍵盤彈出。
總之,若是遇到了上述的問題,不妨能夠試試這樣的辦法。
彈出鍵盤的時候,計算可視區域的高度以及輸入框距離視口的高度加上自己的高度(可視區域、自身距離視口高度 + 自身高度)。若是可視區域的高度大於後者,說明此時的輸入框須要上移,那麼就將body
向上平移,不然不平移。在鍵盤消失的時候迴歸到原來的位置就好。具體能夠看以下代碼,另外能夠看這個例子:https://jsbin.com/ganiqus
var availHeight = Math.max(document.documentElement.clientHeight || document.body.clientHeight); var inputs = document.getElementsByTagName('input'); var textareas = document.getElementsByTagName('textarea'); var footer = document.querySelector('#footer'); var ftStyle = footer.style; var body = document.body; var keyboardHeight; // 綁定事件 for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; input.onfocus = focused; input.onblur = blured; } for (var i = 0; i < textareas.length; i++) { var textarea = textareas[i]; textarea.onfocus = focused; textarea.onblur = blured; } // 模擬事件 function mockEvent(type, fn) { var createDiv = document.createElement('div'); createDiv.style.display = 'none'; document.body.appendChild(createDiv); var e = document.createEvent('MouseEvent'); e.initEvent(type, true, true); createDiv.addEventListener(type, function() { fn && fn(); createDiv.remove(); }); createDiv.dispatchEvent(e); } function focused() { // 事件模擬 mockEvent('native.keyboardshow'); } function blured() { mockEvent('native.keyboardhide'); } function getOffsetTop(el) { var mOffsetTop = el.offsetTop; var mOffsetParent = el.offsetParent; while(mOffsetParent) { mOffsetTop += mOffsetParent.offsetTop + mOffsetParent.clientTop; mOffsetParent = mOffsetParent.offsetParent; } return mOffsetTop; } // 是否須要上移輸入框 function needPullUpScreen(target, top, height) { var keyboardShow = false; var nodeName = target.nodeName; var leftHeight; var isAndroid = true; if (isAndroid) { leftHeight = availHeight - keyboardHeight; } else { leftHeight = availHeight - keyboardHeight * window.devicePixelRatio; } if (nodeName) { if ((top + height + 5) >= leftHeight) { switch (nodeName.toLowerCase()) { case 'input': keyboardShow = target.type !== 'button' && target.type !== 'file'; break; case 'textarea': keyboardShow = true; break; default: keyboardShow = false; break; } } } return keyboardShow; }; // 監聽鍵盤彈出事件 window.addEventListener('native.keyboardshow', function(e) { ftStyle.display = 'none'; // 此處獲取keyboard的高度,由插件提供,這裏寫死 // keyboardHeight = e.keyboardHeight; keyboardHeight = 290; var activeEle = document.activeElement; // getBoundingClientRect 只在android 4.4以上纔有用 // top和height可用getOffsetTop(el)和el.offsetHeight替代 var boundingClientRect = activeEle.getBoundingClientRect(); var top = boundingClientRect.top; var height = boundingClientRect.height; // 移到居中位置 // 這個高度能夠根據本身的狀況來寫 var moveOffset = top + height - availHeight / 2; if (activeEle && needPullUpScreen(activeEle, top, height)) { body.style.webkitTransform = `translate3d(0, ${-moveOffset}px, 0)`; body.style.transform = `translate3d(0, ${-moveOffset}px, 0)`; body.style.webkitTransition = 'transform 200ms'; body.style.transition = 'transform 200ms'; } }); // 監聽鍵盤消失事件 window.addEventListener('native.keyboardhide', function() { body.style.webkitTransform = ''; body.style.transform = ''; body.style.webkitTransition = 'transform 200ms'; body.style.transition = 'transform 200ms'; setTimeout(function() { ftStyle.display = ''; }, 200); });
注意:
APP
的開發中,應該是有相關插件來監聽鍵盤事件的,同時能夠獲取鍵盤的高度getBoundingClientRect
方法,能夠用代碼中提供的getOffsetTop
方法來替代IOS
中也遇到這樣的問題,此時的鍵盤高度要乘以設備像素比