1. 程式人生 > >瀏覽器返回按鈕不會觸發onLoad事件

瀏覽器返回按鈕不會觸發onLoad事件

最近在做一個移動端專案,發現移動端某些返回和PC端是有差異的, 比如ios中返回按鈕是直接使用快取的, 不會執行任何js程式碼, 這個問題很蛋疼, 例如, 在提交的時候將按鈕設定為loading狀態, 如果在提交成功後沒有對按鈕進行處理, 那麼返回後按鈕依然是loading狀態, 這種體驗很差, 如下圖:

此問題是由於某些瀏覽器在back的時候是直接使用的之前的檢視,頁面沒有進行重新載入而導致的,在網上找了些資料, 發現這是H5的一些新特性Back-Forward Cache(簡稱bfcache) ,普通瀏覽器在back時,如果不是指定Cache-Control、Expires等方法強制停用Cache時,那麼一般情況下瀏覽器大多數都會直接讀取本地的快取, 減少請求和網路傳輸的成本, 增加瀏覽的順從度, 但Cache僅限於靜態檔案, 瀏覽器還是得重新載入html, 重新執行指令碼,渲染DOM, 而bfcache則不同, 是直接讀取快取裡面的html,節省了重新請求頁面的時間, 既然是讀取快取的html頁面, 那麼執行頁面的onload事件進行初始化, 會影響原本因使用者操作而改變的狀態, 所以瀏覽器在back時是不會觸發onload事件.

這個時候就會產生上面的問題, 有些業務在返回時是需要重新載入的, 於是H5新增了兩個事件onpageshow和onpagehide, 分別是進入網頁和離開的時候觸發, 即使是用瀏覽器的前進/後退也會觸發這兩個事件.

複製程式碼
 1 <!DOCTYPE html>
 2  
 3 <html>
 4 <head>
 5     <title>Page Events</title>
 6     <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.js"></script
> 7 <script> 8 function dispLog(msg) { 9 var d = new Date(); 10 $("<li />").text(d.toISOString().substr(14, 9) + " " + msg) 11 .appendTo("#dvDisp"); 12 13 } 14 $(window).load(function () { 15 dispLog("Load Event
"); 16 }).ready(function () { 17 dispLog("Ready Event"); 18 $("#btnSetColor").click(function () { 19 $("a").css("color", "red"); 20 }); 21 }).bind("pageshow", function () { 22 dispLog("PageShow Event"); 23 }).bind("pagehide", function () { 24 dispLog("PageHide Event"); 25 }); 26 </script> 27 </head> 28 <body> 29 <a href="test1.html">前往其它頁面</a> 30 <input type="button" id="btnSetColor" value="變色" /> 31 <ul id="dvDisp"></ul> 32 </body> 33 </html>
複製程式碼

頁面很簡單, 繫結onload, ready,onpageshow,onpagehide四個事件, 觸發事件相應的文字會顯示在頁面上, 另外這裡有個連線可跳轉到其它網頁,便於測試back, button事件會改變連線的顏色, 便於back時檢查顏色是否保留,判斷是否有bfcache.

測試步驟開啟test.html, 點選變色按鈕, 再點選"前往其它頁面", 然後在test1.html點選back按鈕回到test.html. 在幾次測試後, 大致的測試結果如下:

複製程式碼
IE9 
開啟頁面或則back時都會觸發Ready/Load事件, 紅色未保留, 無bfcache.
IE10 (Windows 8 Release Preview) 
開啟頁面或則back時都會觸發Ready/Load事件, 紅色未保留, 無bfcache.
Chrome 21.0.1180.6 
開啟頁面或則back時都會觸發Ready/Load/PageShow事件, 紅色未保留, 無bfcache.
Firefox 15.0 
開啟頁面或則back時都會觸發Ready/Load/PageShow事件,點選[前往其它網頁]會觸發PageHide, [back]時會觸發PageShow, 紅色被保留, 有bfcache.
Safari 5.1.5 
開啟頁面或則back時都會觸發Ready/Load/PageShow事件,點選[前往其它網頁]會觸發PageHide, [back]時會觸發PageShow, 紅色被保留, 有bfcache.
Safari on iPad (iOS 5.1.1) 
開啟頁面或則back時都會觸發Ready/Load/PageShow事件,點選[前往其它網頁]會觸發PageHide, [back]時會觸發PageShow, 紅色被保留, 有bfcache.
Opera 12.00 
開啟頁面或則back時都會觸發Ready/Load事件, [back]時會觸發PageShow, 紅色被保留, 有bfcache但不會觸發PageShow事件.
複製程式碼

總結: Firefox和Safari會bfcache, back時不會觸發load, ready事件, 只會觸發onpageshow, 而chrome雖然支援onpageshow, 但是back時一樣都會觸發load,ready事件, opera最操蛋, back時會bfcache,但是不觸發onpageshow事件.

回到上面的問題, 如何解決bfcache時ready在back時不執行的問題呢?

起初是想新增一個$.pageshow(), 若瀏覽器支援, 將業務程式碼放在onpageshow事件裡面處理, 否則用ready處理, 如下:

複製程式碼
 1 $.pageshow = function (fn) {
 2     if (typeof window.onpageshow == "undefined")
 3         $(document).ready(fn);
 4     else
 5         $(window).bind("pageshow", fn);
 6 };
 7 $.pageshow(function () {
 8     alert("Page Show");
 9     alert(typeof window.onpageshow == "undefined")
10 });
複製程式碼

很艹蛋啊, 這個方法只能解決Firefox、Safaer上的問題, 但是在Opera上就沒什麼效果.

還好在MDC的文件上找到一點思路, Firefox在某些條件下禁用bfcache:

複製程式碼
There are instances in which Firefox doesn’t cache pages. Below are some common programmatic reasons that a page is not cached:

the page uses an unload or beforeunload handler;
the page sets "cache-control: no-store".
the site is HTTPS and page sets at least one of:
"Cache-Control: no-cache"
"Pragma: no-cache"
with "Expires: 0" or "Expires" with a date value in the past relative to the value of the "Date" header (unless "Cache-Control: max-age=" is also specified);
the page is not completely loaded when the user navigates away from it or has pending network requests for other reasons (e.g. XMLHttpRequest));
the page has running IndexedDB transactions;
the top-level page contains frames (e.g. <iframe> ) that are not cacheable for any of the reasons listed here;
the page is in a frame and the user loads a new page within that frame (in this case, when the user navigates away from the page, the content that was last loaded into the frames is what is cached).
複製程式碼

想了下如果Firefox可以這樣, Safari和Chrome應該也可以, 於是找到一個非常簡單的方法來解決這個問題, 並且相容Firefox、Safari、Opera, 只要在頁面中加入下面的程式碼:

$(window).unload(function () { });

經過測試, 頁面上一點繫結unload事件, Firefox/Safari/Opera等瀏覽器變會認為該頁面不需要bfcache, 迴歸到傳統的Cache模式, 這樣就能解決back時不觸發onload, ready等事件帶來的問題了.

下面有兩個連線, 不過解決問題的思路都差不多.

http://stackoverflow.com/questions/11979156/mobile-safari-back-button

http://stackoverflow.com/questions/24046/the-safari-back-button-problem