1. 程式人生 > 實用技巧 >IOS移動端 -webkit-overflow-scrollin屬性造成的問題

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)。