Problems with Page Cache in iOS 5 Safari when navigating back / unload event not fired

Clarke picture Clarke · Nov 3, 2011 · Viewed 20.8k times · Source

tl;dr - Safari on iOS 5 is caching so hard, it is breaking my site.

I am struggling with the way the Safari browser in iOS 5 deals with back forward cache, which they call "Page Cache". The way it is described here explains the behavior very well.

Quite simply, the Page Cache makes it so when you leave a page we “pause” it and when you come back we press “play.”

This is causing problems throughout my site. When using the back button, most other browsers will show you the page in the state it was loaded. Not Safari on iOS 5, it shows you the page as you last left it. A simple example would be the disabling of a submit button. If I use Javascript to disable a submit button, then submit a form, when you click back the submit button will still be disabled. This has been an issue in other browsers, including the desktop version of Safari, but it is solved by setting the onload event handler to a blank function. I believe this tells the browser to invalidate the cache because something important could have happened in that function. This hack does not seem to work for Safari on iOS 5.

Below is the issue boiled down to the bare essentials. When you load test.html you will see the text "Original Text". When you click the link, that text will change to "Changed text - forwarding to next page", then in 3 seconds you will be forwarded to test2.html. All is good up to this point in all browsers. In all other browsers, when you click the back button, the text you will see is "Original Text", but on Safari for iOS 5 you will see "Changed text - forwarding to next page".

Any suggestions on how to deal with this?

This is a simple example

test.html

<script>
function changeText() {
    el = document.getElementById("text");
    el.innerHTML = "Changed text - forwarding to next page";
    setTimeout("forward()",3000);       
}
function forward() {
    document.location.href = "test2.html";
}
</script>
<div id="text">Original Text</div>
<a href="Javascript:changeText()">Click Here</a>
<script>
window.onunload = function(){};
</script>

test2.html

<div>Click back button</div>

This is a second example using a form post. This is a simple example of how my app is working. When you navigate back to formtest2.asp, you should see the posted form value and the div text should be original.

formtest.asp

<form method="post" action="formtest2.asp">
    Test: <input type="text" name="test"/>
    <input type="submit" value="Submit"/>
</form>

formtest2.asp

<script>
function changeText() {
    el = document.getElementById("text");
    el.innerHTML = "Changed text - forwarding to next page";
    setTimeout("forward()",3000);       
}
function forward() {
    document.location.href = "test2.html";
}
</script>

<%
Dim test
test = Request("test")
Response.Write("Test value: " & test & "<br />")
%>

<div id="text">Original Text</div>
<a href="Javascript:changeText()">Click Here</a>
<script>
window.onunload = function(){};
</script>

test2.html

<div>Click back button</div>

Answer

BrutalDev picture BrutalDev · Mar 20, 2012

I was also looking for an answer to the same issue. Going back in iOS 5 does not execute any javascript and just leaves the page in the previous state it was when you left or were redirected.

Trying the weird "onunload" hack found in "Is there a cross-browser onload event when clicking the back button?" only worked for iOS 4, not iOS 5 which does not call the event as expected. Nickolay pointed out new functions on web-kit called "pageshow" and "pagehide" which are much more reliable than Giuseppe's example.

  1. You need the hack from this article: "Is there a cross-browser onload event when clicking the back button?" for this to work in iOS 4.
  2. Use this script which uses the new event to safely check if the page was loaded from cache and force a reload (avoiding URLs checks and local storage and also include iPods) for iOS 5:

    <body onunload="">
    ...
    <script type="text/javascript">
    if ((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)) {
      window.onpageshow = function(evt) {
        // If persisted then it is in the page cache, force a reload of the page.
        if (evt.persisted) {
          document.body.style.display = "none";
          location.reload();
        }
      };
    }
    </script>