前幾天在幫一個同事看了個問題,就是他的老專案裡(用的jquery)寫的,用iScroll外掛做滾動載入的時候,在他的iphone7手機上,指定區域只能移動6px左右,這明顯不正常,其他同事包括我的(8plus)是正常的,然後幫他找原因, 首先檢視了下iScroll的版本4.25,好像也沒問題啊。
一、 問題分析
1、onBeforeScrollStart方法:
onBeforeScrollStart: function(e) {
e.preventDefault();
}
複製程式碼
此方法是在_start裡進行了呼叫,目的是為了阻止瀏覽器預設動作的執行,防止在滑動的過程中進行干擾,同時也就阻止了滑動區域裡元素的事件的觸發,這種處理方式也直接導致了必須要在_end方法中再次觸發元素的點選事件。
2、_end方法
if (target.tagName != 'SELECT' && target.tagName != 'INPUT'
&& target.tagName != 'TEXTAREA') {
ev = document.createEvent('MouseEvents');
ev.initMouseEvent('click', true, true,
e.view, 1,point.screenX,
point.screenY, point.clientX,
point.clientY,e.ctrlKey,
e.altKey, e.shiftKey,
e.metaKey,0, null);
ev._fake = true;
target.dispatchEvent(ev);
}
複製程式碼
這個處理方式就是順承了上面所提到的阻止了瀏覽器預設行為後,對滑動區域除select、input、textarea外的元素觸發click事件,已完成對click繫結事件的呼叫。
二、 具體分析
由於onBeforeScrollStart是在_start方法中進行的呼叫,e.preventDefault();阻止了元素的預設行為,從而導致了元素繫結事件的失效,必須在_end操作結束後進行繫結事件的模擬呼叫,原始的iScroll原始碼中在_end中最後建立了click事件的模擬,但是這裡必須要清楚的一個原理就是,click其實是要依賴於其他事件的:
-
1 普通pc網頁中,click需要依賴於mousedown、mouseup的相繼觸發
-
2 移動webkit中,click則需要依賴於touchstart、touchend(實際mousedown,mouseup在移動webkit上也存在)的相繼觸發 “相繼觸發”的意思就是中間不會夾雜有其他的事件型別,這也就很容易理解iScroll中在_end中對模擬事件呼叫的條件了,必須要判斷that.moved才能直接觸發模擬事件。
iScroll中與_start、_move、_end相關的三個事件型別是按照如下的規則來設定的:
START_EV = hasTouch ? 'touchstart' : 'mousedown'
MOVE_EV = hasTouch ? 'touchmove' : 'mousemove'
END_EV = hasTouch ? 'touchend' : 'mouseup'
複製程式碼
三、 具體說明
經測試,部分手機裡預設瀏覽器裡會預設所有元素都有一個預設的click事件(測試結果顯示滑鼠事件中預設事件包含mouseup,mousedown,click,dblclick在移動webkit上不支援,為系統放大功能),e.preventDefault時會阻止掉預設click事件的執行,必須要人為的在_end結束之後模擬click事件的呼叫,而其他正常手機即使呼叫了e.preventDefault也不會阻止click事件的觸發,因為這些手機的預設瀏覽器上的元素的click事件的cancelable屬性不為true,不可以被preventDefault取消掉,會正常執行,而如果在_end中模擬了click事件則將會導致click的重複呼叫(在有toggle狀態的事件上非常明顯),因此折中的方式參見下面的解決方案。
四、 解決方案
1. 去除onBeforeScrollStart裡的阻止預設行為:
onBeforeScrollStart: function (e) {
//e.preventDefault();
}
複製程式碼
2.onBeforeScrollMove設定為:
onBeforeScrollMove:function (e) {
e.preventDefault();
}
複製程式碼
以保證手機上的正常滑動免受瀏覽器預設行為影響(如下滑時會有視窗的scroll事件),當然如果這裡不新增的話也可以在document的END_EV中阻止瀏覽器預設行為
3. _end 中將模擬事件名更改為END_EV或者直接去掉模擬事件的功能
iscroll.js是Matteo Spinelli開發的一個js檔案,使用原生js編寫,不依賴與任何js框架。旨在解決移動webkit系瀏覽器的區域滾動問題,相容mobile、safari、android預設瀏覽器、safari、chrome、firefox5+、opera11+、IE9+及其他webkit核心瀏覽器。
5、最後說明
最後,我在此次解決滾動失效的過程中,針對該頁面做了一個最簡單的處理如下:
// 獲取所需要滾動區域的元素
var scrollPage = document.querySelector('.scrollPage');
// 取消事件預設動作
scrollPage.addEventListener('touchmove',function(e){
e.preventDefault();
});
複製程式碼
這樣頁面即可恢復滾動效果了。