在做移動端的需求有時候需要用到對滾動結束進行事件綁定以完成一定的功能,先來了解一下現狀。
在移動端,正常的一次swipe動作會依照以下順序觸發事件:
touchstart -> touchmove -> touchend ->scroll
一、IPAD
通過以下代碼進行測試:
var timestart =0; var timer = null; function log(){ console.log("---------settimeout" + (new Date()-timestart) ); } $(window).on('scroll',function(){ console.log('-----scrollend:' + (new Date() - timestart) +'ms'); timestart = new Date(); }); $(window).on('touchstart',function(){ console.log('touchstart'); }); $(window).on('touchmove',function(){ console.log('touchmove'); }); $(window).on('touchend',function(){ timestart = new Date(); console.log('touchend'); timer = setTimeout(log,1); })
當“tap”時,依次產生如下事件:
touchstart -> touchend
當"swipe"時,依次產生如下事件:
touchstart -> touchmove * 多次 -> touchend -> scroll
在ipad中,touchend事件在手指離開時觸發,由於是swipe動作,所以在手指離開后,屏幕還會滾動一段時間,直至最后停止,在最后的停止事件會觸發scroll事件。
可見,在ipad中,滾動結束的判斷只需要監聽scroll事件即可。
那么從touchend到scroll這段時間如何操作dom呢?使用以下測試代碼,一次swipe從touchend到scroll的時間大概經過0-2000ms不等(也許可能更長,依據加速度)。現在向touchend中添加setTimeout事件,設定延遲時間為1ms(幾乎一定在scroll之前)。實驗了四次,得到結果如下(使用weinre遠程調試)

可以從結果中看到,在ipad中,settimeout並沒有如預想的在scrollend之前觸發,反而在scrollend觸發后才觸發。
由這種情況可以推測,事件隊列中的順序為 touchend回調 -> scroll回調 -> settimeout函數。
所以,如果想操作touchend到scroll這段時間的動作,在ipad下是不可實現的。
二、Android
依舊使用上述測試代碼。
當“tap”時,依次產生如下事件:
touchstart -> touchend
當"swipe"時,依次產生如下事件:
touchstart -> touchmove -> scroll*多次
另外需要注意的是,在android下,swipe雖然不會觸發touchend事件,但是會在scroll事件之前觸發一次touchcancel事件,即:
touchstart -> touchmove -> touchcancel->scroll*多次
在swipe過程中,並沒有觸發touchend事件,同時卻觸發了多次scroll事件,並在第一次觸發scroll事件時有大概1000ms的延時。即使手指在屏幕上來回滑動,也只觸發一次touchmove事件,后續過程均為scroll事件。
所以,如果需要做兼容性良好的滾動結束的判斷,是不能根據touchend事件的觸發做判斷的,同時,由於scroll事件在Android中會觸發多次,而在蘋果中只觸發一次(最后停止時),所以在對scroll進行事件綁定判斷時,需要通過setTimeout的方法不斷判斷當前scrollTop與舊的scrollTop的值是否一致,如果不一致則說明屏幕仍在滾動。實現代碼如下:
var count = 0, timer = null; var oldTop = newTop = $(window).scrollTop(); //為了方便起見,使用jquery或者zepto框架 function log(){ if(timer) clearTimeout(timer); newTop = $(window).scrollTop(); console.log(++count,oldTop,newTop); if(newTop === oldTop) { clearTimeout(timer); //已停止,寫入業務代碼 } else{ oldTop = newTop; timer = setTimeout(log,100); } } $(window).on('scroll',log);
然而憂桑的是,據說很多android的機型在觸發scroll事件的時候,仍舊不能遵守規則按照正確的方式觸發,所以如果我們監聽scroll事件,仍舊有幾率出bug,so結論是~~~~
三、結論
針對ipad和Android的滾動結束監聽,其兼容性處理方案為:監聽touchmove事件
來保證最好的兼容。即代碼如下:
var count = 0, timer = null; var oldTop = newTop = $(window).scrollTop(); //為了方便起見,使用jquery或者zepto框架 function log(){ if(timer) clearTimeout(timer); newTop = $(window).scrollTop(); console.log(++count,oldTop,newTop); if(newTop === oldTop) { clearTimeout(timer); //已停止,寫入業務代碼 } else{ oldTop = newTop; timer = setTimeout(log,100); } } $(window).on('touchmove',log);
這種方式由於過早的監聽可能會損耗更多的性能,所以如果你只做ipad的話就監聽scroll(或touchend);只做android就監聽touchcancel。
