瀏覽器在回上頁時不會觸發網頁onLoad事件
3 |
最近參與的一個ASP.NET MVC行動網站專案,其中有項操作是由清單頁面按連結跳往編輯網頁,編輯完成後,透過jQuery Mobile內建的回上頁功能回到清單頁面。而清單頁面中透過$(function() { ajaxLoadListData() });的做法,預期每次網頁載入後[jQuery.ready()事件]都會以AJAX方式重新取得最新清單資料,因此回上頁到清單,使用者就可立即看到剛才儲存的變更。
使用IE及Chrome測試功能正常,但進一步使用iPhone/iPad Safari瀏覽時,卻發現編輯完回上頁時不會自動載入更新後的資料,需待重新整理才現身。研究後才理解了一件先前忽略的事實:
部分瀏覽器在回上頁時不會觸發jQuery .ready()/.load()事件
進一步發現,原來這是HTML5世代瀏覽器新增的特性之一--Back-Forward Cache(簡稱bfcache)。傳統瀏覽器在回上頁時,除非是透過指定Cache-Control、Expires等方式強制停用Cache的網頁,否則會直接讀取保存在本機的HTML、CS、JS快取檔案,減少網路往返傳輸,增進瀏覽順暢度。但Cache的層次限於檔案,瀏覽器還是得載入HTML,重新執行JavaScript、產生DOM。而bfcache則更進一步,直接引用在記憶體中先前做好的網頁內容,省去重新產生網頁的時間;既然要使用記憶體中的現成內容,執行頁面的onload事件進行初始化,會弄亂原本因使用者操作而改變的狀態,瀏覽器在回上頁時不觸發onload事件也是合理的。
但程式開發上,還是需要有個事件察覺使用者從其他網頁跳回這個頁面,於是HTML5規格中有兩個新事件onpageshow及onpagehide,會分別在使用者進入及離開網頁時被觸發--即使透過回上頁/到下頁巡覽進出該網頁也會觸發。
我寫了一個網頁來檢測各版本瀏覽器的行為: (線上展示)
<!DOCTYPE html>
<html>
<head>
<title>Page Events</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.js"></script>
<script>
function dispLog(msg) {
var d = new Date();
$("<li />").text(d.toISOString().substr(14, 9) + " " + msg)
.appendTo("#dvDisp");
}
$(window).load(function () {
dispLog("Load Event");
}).ready(function () {
dispLog("Ready Event");
$("#btnSetColor").click(function () {
$("a").css("color", "red");
});
}).bind("pageshow", function () {
dispLog("PageShow Event");
}).bind("pagehide", function () {
dispLog("PageHide Event");
});
</script>
</head>
<body>
<a href="Another.htm">前往其他網頁</a>
<input type="button" id="btnSetColor" value="變色" />
<ul id="dvDisp"></ul>
</body>
</html>
網頁很簡單,透過jQuery掛載了load(), ready(), onpageshow,onpagehide四個事件,事件被觸發時會顯示訊息在網頁上。另外有個超連結可跳至其他網頁,方便測試回上頁跳回的情境,有個按鈕按下後會將超連結變成紅色,藉此在回上頁時觀察超連結是否仍保持紅色,便可判定有無bfcache。
測試步驟為先連上PageEvent.htm,按下【變色】按鈕使超連結變色,再點選【前往其他網頁】跳至Another.htm;接著在Another.htm按【回上頁】鈕回到PageEvent.htm。在不更動瀏覽器預設設定的情況,測試結果如下:
- IE9
新載入或回上頁時都會觸發Ready/Load事件,紅色未保留,無bfcache - IE10 (Windows 8 Release Preview)
新載入或回上頁時都會觸發Ready/Load事件,紅色未保留,無bfcache - Chrome 21.0.1180.6
新載入或回上頁時都會觸發Ready/Load/PageShow事件,紅色未保留,無bfcache - Firefox 15.0
新載入時觸發Ready/Load/PageShow,點【前往其他網頁】時觸發PageHide,【回上頁】時觸發PageShow,紅色被保留,有bfcache - Safari 5.1.5
新載入時觸發Ready/Load/PageShow,點【前往其他網頁】時觸發PageHide,【回上頁】時觸發PageShow,紅色被保留,有bfcache - Safari on iPad (iOS 5.1.1)
新載入時觸發Ready/Load/PageShow,點【前往其他網頁】時觸發PageHide,【回上頁】時觸發PageShow,紅色被保留,有bfcache - Opera 12.00
新載入會觸發Ready/Load事件,回上頁時不觸發任何事件且紅色被保留,有bfcache但沒有onpageshow事件
歸納結果,Firefox與Safari已實做bfcache,回上頁時不會觸發load(), ready(),只會觸發onpageshow,而Chrome雖然支援onpageshow,但回上頁時跟IE一樣都會觸發load()、ready()。Opera最麻煩,回上頁時會啟用bfcache,卻不提供onpageshow事件。
回到我遇到的案例上,要怎麼克服bfcache模式下.ready()在回上頁時不會執行的問題呢?
最初的想法是新增一個$.pageshow()方法,若瀏覽器支援,將程式掛在onpageshow事件,否則改用傳統.ready()觸發,如下例:
$.pageshow = function (fn) {
if (typeof window.onpageshow == "undefined")
$(document).ready(fn);
else
$(window).bind("pageshow", fn);
};
$.pageshow(function () {
alert("Page Show");
alert(typeof window.onpageshow == "undefined")
});
不過很遺憾,這個做法只在Firefox、Safaer上策效,對回上頁時不呼叫.ready()也不支援onpageshow的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
orbeforeunload
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都適用的做法,只要在網頁中多加一行jQuery程式: (線上展示)
$(window).unload(function () { });
經實地測試,網頁一旦掛上任何onunload事件,Firefox/Safari/Opera等瀏覽器便會判定該網頁不適用bfcache,回歸傳統Cache模式,就能避開回上頁不觸發.load()或.ready()事件的困擾囉~
Comments
# by Pinky
我遇到的問題是:我連結到下一頁時,有些被隱藏的按鈕會出現,有的設計清除的按鈕也沒辦法清除。這個是什麼原因呢?
# by Eric3917
Pinky 試試在連結中,使用 rel="external" <a href="@Url.Content("~/")" data-icon="home" rel="external" data-iconpos="notext" data-direction="reverse" class="ui-btn-left jqm-home">Home</a> 我也是初學者,不曉得能不能幫到你.....
# by Paul
Hello, 有照所說加入 <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.js "></script> <script>$(window).unload(function () { });</script> 測試之後目前只有Safari瀏覽器回到前一頁仍不行,其他如Firefox測試結果都可以!? 有解決辦法嗎?