50

I do some minor programming and web work for a local community college. Work that includes maintaining a very large and soul destroying website that consists of a hodge podge of VBScript, javascript, Dreamweaver generated cruft and a collection of add-ons that various conmen have convinced them to buy over the years.

A few days ago I got a call "The website is locking up for people using Safari!" Okay, step one download Safari(v3.1.2), step two surf to the site. Everything appears to work fine.

Long story short I finally isolated the problem and it relates to Safari's back button. The website uses a fancy-pants javascript menu that works in every browser I've tried including Safari, the first time around. But in Safari if you follow a link off the page and then hit the back button the menu no longer works.

I made a pared down webpage to illustrate the principle.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>Safari Back Button Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body onload="alert('Hello');">
<a href="http://www.codinghorror.com">Coding Horror</a>
</body>
</html>

Load the page and you see the alert box. Then follow the link off the page and hit the back button. In IE and Firefox you see the alert box again, in Safari you do not.

After a vigorous googling I've discovered others with similar problems but no really satisfactory answers. So my question is how can I make my pages work the same way in Safari after the user hits the back button as they do in other browsers?

If this is a stupid question please be gentle, javascript is somewhat new to me.

Autodidact
  • 768
  • 1
  • 9
  • 11
  • Hey Auto, have you figured this out yet? I've tried all of the solutions below on iPads and iPods, and I can't get Safari to run the JavaScript when the client presses the back button. (I can get it when I try it with Safari on a desktop, but I can't get it to work with a mobile version of Safari. Is this something you worked with at all?) – Sal Aug 15 '12 at 23:22
  • Check the answer on question [Mobile Safari back button](http://stackoverflow.com/questions/11979156/mobile-safari-back-button/12652160) – Mika Tuupola Sep 29 '12 at 11:25
  • onload() event is not firing in Safari when back button is clicked – Mladen Janjetovic May 15 '14 at 08:45
  • Sal I did figure this out eventually. I called my function with jquery's "document.ready" instead of "onload". – Autodidact Jul 30 '15 at 15:02
  • I found my answer here https://guwii.com/cache-issues-with-forwards-and-back-history-in-safari/ – Mahmood Jun 08 '21 at 18:32
  • Just in case users run into this question still today: learn about **bfcache** (e.g. via [this blog post](https://web.dev/bfcache/)) and see [here](https://stackoverflow.com/a/68606072/3991164) how to hook in some code to tidy up the prior page state. – flaschbier Jul 31 '21 at 22:13

12 Answers12

19

Stefan's iframe solution works, but if that's not elegant enough, I find the following JavaScript also solves it:

window.onunload = function(){};

That is, if your menu is JavaScript, then you might prefer to solve this issue with JavaScript too.

The unload event handler definition idea came from this Firefox 1.5 article: https://developer.mozilla.org/en/Using_Firefox_1.5_caching.

Lee Kowalkowski
  • 11,591
  • 3
  • 40
  • 46
  • I had problems with the back button in Safari even though I WAS using jQuery's $(document).ready event. I added the above code and it resolved the issue. – Ben Foster Feb 01 '11 at 16:27
  • Seems like this Q&A is still relevant. When considering browser quirks, one is inclined to think of IE only, but the 'others' (Webkit, Firefox) have them as well, albeit more minor _and_ more elusive... – kasimir Mar 01 '12 at 08:44
  • 1
    @Mika Tuupola: Safari is not only available on iOS. I don't see how the operating system has any part to play in Safari's behaviour, you must have a newer version of Safari too. I don't have any iOS devices, so *shrug*. – Lee Kowalkowski Sep 29 '12 at 19:07
  • 1
    @LeeKowalkowski the OS does have an effect on it - Safari for iOS **does not** work, while the OS X version **does** work! – Jesse Apr 07 '13 at 12:05
  • @Jesse: Then they're obviously different beasts! I think I'll delete this answer though. I can't really help people that don't see a problem using the `onload` event in the first place (hint: it's not a great event, and probably not what you think it is, i.e: it triggers too late) – Lee Kowalkowski Apr 07 '13 at 12:26
  • @LeeKowalkowski No need to delete it! You've got enough votes on it to merit the answer some value! Just specify that *the provided fix has been known to only work on Firefox, but iOS Safari requires something different*. – Jesse Apr 07 '13 at 12:35
  • Great! That solved my problem of Safari not firing onload() event when back button is clicked – Mladen Janjetovic May 15 '14 at 08:43
  • In 2021 looks that is not working anymore. Any suggestion? – Aral Roca Feb 04 '21 at 17:57
  • 2
    @AralRoca Yes. Check out about **bfcache** (e.g. via [this blog post](https://web.dev/bfcache/)) and see [here](https://stackoverflow.com/a/68606072/3991164) how to hook in some code to tidy up the prior page state. – flaschbier Jul 31 '21 at 22:17
11

Just put this in body tag <body onunload="">

this will force safari, chrome, FF reload every time you hit Back button

Manuel Pardo
  • 127
  • 1
  • 2
9

Please do not follow any of the advice that tells you to ignore the cache. Pages are cached for a reason -- to improve user experience. The methods you're using will make user experience worse, so unless you hate your users, don't do that.

The correct solution for Safari (Desktop and iOS) is to use the pageshow event instead of the onload event (See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching for what these are).

The pageshow event will fire at the same time you expect the onload event to fire, but it will also work when pages are served via the cache. This appears to be what you want anyway.

bluesmoon
  • 3,918
  • 3
  • 25
  • 30
  • 3
    Really? If js modified a page somehow, for example to indicate a spinner on a button that indicates process, and then new page loaded, then on back navigation you will see this spinner on the button. And that's not the way it should be – Sergey P. aka azure Jul 22 '16 at 09:29
  • 1
    @SergeyP.akaazure that's why you use the `pagehide` event to stop the spinner. – bluesmoon Jul 22 '16 at 13:23
8

Here is a good solution for Mobile Safari:

/*! Reloads page on every visit */
function Reload() {
    try {
        var headElement = document.getElementsByTagName("head")[0];
        if (headElement && headElement.innerHTML)
            headElement.innerHTML += " ";
        } catch (e) {}
    }

    /*! Reloads on every visit in mobile safari */
    if ((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)) {
        window.onpageshow = function(evt) {
            if (evt.persisted) {
                document.body.style.display = "none";
                location.reload();
            }
        };
    }

(source)

I modified it to my needs, but as is it is okay. (annoying white screen on refresh if you dont modify it).

Littm
  • 4,923
  • 4
  • 30
  • 38
Gary Hayes
  • 1,728
  • 1
  • 15
  • 23
  • Lone link is [considered a poor answer](http://stackoverflow.com/faq#deletion) since it is meaningless by itself and target resource is not guaranteed to be alive in the future. Please try to include at least summary of information you are linking to. – j0k Sep 06 '12 at 07:17
7

An iframe solves the problem:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>Safari Back Button Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body onload="alert('Hello');">
<a href="http://www.codinghorror.com">Coding Horror</a>
<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
this prevents back forward cache
</iframe>
</body>
</html>

more details

Stefan Ladwig
  • 569
  • 9
  • 19
  • 1
    The link in this answer is now broken; you can find [the details archived here](http://web.archive.org/web/20070612072521/http://developer.apple.com/internet/safari/faq.html#anchor5). – Simon Lieschke Jan 21 '11 at 02:40
4
$(window).bind("pageshow", function(event) {
  if (event.originalEvent.persisted) {
    window.location.reload() 
  }
});

Answered more recently here by mshah.

Community
  • 1
  • 1
ztech
  • 560
  • 5
  • 13
3

I have no idea what's causing the problem but I know who might be able to help you. Safari is built on Webkit and short of Apple (who are not so community minded) the Webkit team might know what the issue is.

It's not a stupid question at all.

Teifion
  • 108,121
  • 75
  • 161
  • 195
3

I've noticed something very similar. I think it is because Firefox and IE, when going back, are retrieving the page from the server again and Safari is not. Have you tried adding a page expiry/no cache header? I was going to look into it when I discovered the behaviour but haven't had time yet.

Stacey Richards
  • 6,536
  • 7
  • 38
  • 40
1

I know this thread is a year old, but I just fixed an identical Safari-only problem using ProjectSeven's Safari backbutton fix. Works like a charm.

http://www.projectseven.com/extensions/info/safaribbfix/index.htm

brunam
  • 825
  • 2
  • 14
  • 26
  • 1
    Its funny because ProjectSeven was the javascript menu I was talking about. Eventually ditched it for a homemade jquery one. Moving from onload in the body tag to jquery's document.ready solved the issue for me. – Autodidact Jun 01 '10 at 21:00
0

Had the same problem on iPad.

Not that beautiful but it works :). How it works.

I realised that on iPad Safari, the page was not reloaded when the back button was pressed. I put a counter every second on the page and I save the current timestamp.

When the page is loaded the counter and time are synchronized. On back button, counter continue where it stopped and there is a gap between timestamp and counter. If the gap is grater than 500ms, force reload the page.

In the file action.js

var showLoadingBoxSetIntervalVar;
var showLoadingBoxCount = 0;
var showLoadingBoxLoadedTimestamp = 0

function showLoadingBox(text) {

    var showLoadingBoxSetIntervalVar=self.setInterval(function(){showLoadingBoxIpadRelaod()},1000);
    showLoadingBoxCount = 0
    showLoadingBoxLoadedTimestamp = new Date().getTime();

    //Here load the spinner

}

function showLoadingBoxIpadRelaod()
{
    //Calculate difference between now and page loaded time minus threshold 500ms
    var diffTime = ( (new Date().getTime()) - showLoadingBoxLoadedTimestamp - 500)/1000;

    showLoadingBoxCount = showLoadingBoxCount + 1;
    var isiPad = navigator.userAgent.match(/iPad/i) != null;

    if(diffTime > showLoadingBoxCount && isiPad){
        location.reload();
    }
}
ArnaudBB
  • 9
  • 2
0

Try this
if(performance.navigation.type == 2) {
alert("Hello");
}

  • While this might be a valuable hint to solve the problem, a good answer also demonstrates the solution. Please [EDIT](http://stackoverflow.com/posts/5419867/edit) to provide example code to show what you mean. Alternatively, consider writing this as a comment instead – ρяσѕρєя K Jan 09 '20 at 13:21
0

In my Angular application I used this condition to fix the problem. First I added onunload="" event handle to index.html lilke below

<body class="mat-typography" onunload="">
    <app-root></app-root>
</body>

then in my component ngOnInit method I added that code like below

  public ngOnInit(): void {
    window.onpageshow = (event) => {
        if (event.persisted) {
            document.body.style.display = "none";
            location.reload();
        }
    };
    ...
  }
Samet ÇELİKBIÇAK
  • 895
  • 3
  • 12
  • 25