IOS移動端 -webkit-overflow-scrollin屬性造成的問題
-webkit-overflow-scrolling帶來的相關問題。
-webkit-overflow-scrolling 屬性控制元素在移動裝置上是否使用滾動回彈效果.
其具有兩個屬性:
auto: 使用普通滾動, 當手指從觸控式螢幕上移開,滾動會立即停止。
touch: 使用具有回彈效果的滾動, 當手指從觸控式螢幕上移開,內容會繼續保持一段時間的滾動效果。繼續滾動的速度和持續的時間和滾動手勢的強烈程度成正比。同時也會建立一個新的堆疊上下文。
在查詢相關資料時,有看見這樣一個問題,雖然在開發過程中並沒有遇見,但防患未然還是做一個記錄。
如果在-webkit-overflow-scrolling:touch屬性的元素上,想通過動態新增內容來撐開容器,觸發滾動,頁面會卡住不動。(測試中未復現此bug)。
給出的解決方案:給內容最小高度101%;主動觸發scrollbar。
你也可以直接加偽元素上
滾動的盒子:after { min-height: calc(100% + 1px)}
OS回彈現象產生的問題
滾動元素的滾動條在頂部時做下拉操作,或滾動條在底部時做上滑操作時,引發視窗反彈。此時滾動元素的滾動事件暫時失效。
解決方案可引用inobounce.js,其原理為:
判斷當前瀏覽器是否支援-webkit-overflow-scrolling:touch屬性。
若存在,監聽touchstart和touchmove事件
window.addEventListener('touchstart ',handleTouchstart, { passive : false }); window.addEventListener('touchmove',handleTouchmove,{ passive : false });
在touchstart中獲取觸控點的y(startY)座標
在touchmove中通過迴圈查詢,如果當前事件源的祖先元素具有-webkit-overflow-scrolling:touch屬性,且完全計算屬性overflow-yauto或者overflow-yscroll 則進一步做判斷,否則阻止預設事件。
var handleTouchmove = function(evt) var el = evt.target; //獲取當前事件源 var zoom = window.innerWidth / window.document.documentElement.clientWidth; if (evt.touches.length > 1 || zoom !== 1) { return; } //檢查是否存在可滾動的祖先元素 while (el !== document.body && el !== document) { //獲取樣式屬性 var style = window.getComputedStyle(el); if (!style) { // 如果遇到無法計算樣式的元素,請退出 break; } //忽略input元素 if (el.nodeName === 'INPUT' && el.getAttribute('type') === 'range') { return; } var scrolling = style.getPropertyValue('-webkit-overflow-scrolling'); var overflowY = style.getPropertyValue('overflow-y'); var height = parseInt(style.getPropertyValue('height'), 10); var isScrollable = scrolling === 'touch' && (overflowY === 'auto' || overflowY === 'scroll'); var canScroll = el.scrollHeight > el.offsetHeight; // 如果檢查的元素具有回彈屬性,且可滾動。 if (isScrollable && canScroll) { //獲取當前觸控點的y( curY )座標 var curY = evt.touches ? evt.touches[0].screenY : evt.screenY; //滾動條位於頂部,且使用者進行下拉操作 var isAtTop = (startY <= curY && el.scrollTop === 0); //滾動條位於底部,且使用者進行上滑操作,此時視窗也會反彈 var isAtBottom = (startY >= curY && el.scrollHeight - el.scrollTop === height); // 這兩種情況下視窗會都會反彈,為避免這種現象,此時阻止預設事件 if (isAtTop || isAtBottom) { evt.preventDefault(); } return; } // 測試元素不具有回彈屬性,且不可滾動,繼續測試其父元素 el = el.parentNode; } // 祖先元素都不具有回彈屬性,且不可滾動,則阻止預設事件 evt.preventDefault(); };
Element.scrollHeight 這個只讀屬性是一個元素內容高度的度量,包括由於溢位導致的檢視中不可見內容。
以上方式雖然可解決當滾動條到達底部或頂部時,視窗整體下移或上移,導致內部滾動事件暫時性失效問題
不過也使得滾動容器自身回彈效果的部分丟失。
可改進的一個想法的記錄:
在touchstart事件中判斷祖先元素是否具有回彈屬性。且當滾動條在頂部或底部時對應的將滾動條的位置置為1或底部滾動最大距離-1。
阻止其他自身或祖先元素不具有滾動條的元素的預設事件。
在做測試時發現在非輕觸時是可以實現的,但是觸控事件急促短暫時,視窗依舊整體下移。
移動端喚起軟鍵盤引起的問題
當input框聚焦時,移動端喚起軟鍵盤,頁面整體太高,當軟鍵盤消失後,如果input框的祖先元素是固定定位,則元素視覺上回到之前的位置,但實際上視窗的滾動條為負,頁面出現錯位現象,如果祖先元素是絕對定位,元素會停留在軟鍵盤存在時的位置,需手動滑動頁面恢復原狀。
解決方法:中心思想是在失焦事件時設定window.scroll(0,0)。
但是,當頁面記憶體在兩個以上的輸入框時,在輸入框切換之間,會存在聚焦失焦聚焦事件,如果未做判斷,此時會出現輸入框切換間頁面上下抖動。
解決方式:在失焦事件中設定定時器,定時器內部設定window.scroll(0,0)在聚焦事件中清除定時器。即保證了在軟鍵盤存在時不觸發window.scroll(0,0)。