Prevent safari loading from cache when back button is clicked - javascript

Got an issue with safari loading old youtube videos when back button is clicked. I have tried adding onunload="" (mentioned here Preventing cache on back-button in Safari 5) to the body tag but it doesn't work in this case.
Is there any way to prevent safari loading from cache on a certain page?

Your problem is caused by back-forward cache. It is supposed to save complete state of page when user navigates away. When user navigates back with back button page can be loaded from cache very quickly. This is different from normal cache which only caches HTML code.
When page is loaded for bfcache onload event wont be triggered. Instead you can check the persisted property of the onpageshow event. It is set to false on initial page load. When page is loaded from bfcache it is set to true.
Kludgish solution is to force a reload when page is loaded from bfcache.
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload()
}
};
If you are using jQuery then do:
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
window.location.reload()
}
});

All of those answer are a bit of the hack. In modern browsers (safari) only on onpageshow solution work,
window.onpageshow = function (event) {
if (event.persisted) {
window.location.reload();
}
};
but on slow devices sometimes you will see for a split second previous cached view before it will be reloaded. Proper way to deal with this problem is to set properly Cache-Control on the server response to one bellow
'Cache-Control', 'no-cache, max-age=0, must-revalidate, no-store'

Yes the Safari browser does not handle back/foreward button cache the same like Firefox and Chrome does. Specially iframes like vimeo or youtube videos are cached hardly although there is a new iframe.src.
I found three ways to handle this. Choose the best for your case.
Solutions tested on Firefox 53 and Safari 10.1
1. Detect if user is using the back/foreward button, then reload whole page or reload only the cached iframes by replacing the src
if (!!window.performance && window.performance.navigation.type === 2) {
// value 2 means "The page was accessed by navigating into the history"
console.log('Reloading');
//window.location.reload(); // reload whole page
$('iframe').attr('src', function (i, val) { return val; }); // reload only iframes
}
2. reload whole page if page is cached
window.onpageshow = function (event) {
if (event.persisted) {
window.location.reload();
}
};
3. remove the page from history so users can't visit the page again by back/forward buttons
$(function () {
//replace() does not keep the originating page in the session history,
document.location.replace("/Exercises#nocache"); // clear the last entry in the history and redirect to new url
});

You can use an anchor, and watch the value of the document's location href;
Start off with http://acme.co/, append something to the location, like '#b';
So, now your URL is http://acme.co/#b, when a person hits the back button, it goes back to http://acme.co, and the interval check function sees the lack of the hash tag we set, clears the interval, and loads the referring URL with a time-stamp appended to it.
There are some side-effects, but I'll leave you to figure those out ;)
<script>
document.location.hash = "#b";
var referrer = document.referrer;
// setup an interval to watch for the removal of the hash tag
var hashcheck = setInterval(function(){
if(document.location.hash!="#b") {
// clear the interval
clearInterval(hashCheck);
var ticks = new Date().getTime();
// load the referring page with a timestamp at the end to avoid caching
document.location.href.replace(referrer+'?'+ticks);
}
},100);
</script>
This is untested but it should work with minimal tweaking.

The behavior is related to Safari's Back/Forward cache. You can learn about it on the relevant Apple documentation: http://web.archive.org/web/20070612072521/http://developer.apple.com/internet/safari/faq.html#anchor5
Apple's own fix suggestion is to add an empty iframe on your page:
<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
this frame prevents back forward cache
</iframe>
(The previous accepted answer seems valid too, just wanted to chip in documentation and another potential fix)

I had the same issue with using 3 different anchor links to the next page. When coming back from the next page and choosing a different anchor the link did not change.
so I had
House 1
View House 2
View House 3
Changed to
House 1
View House 2
View House 3
Also used for safety:
// Javascript
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload()
}
};
// JQuery
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
window.location.reload()
}
});
None of the solutions found online to unload, reload and reload(true) singularily didn't work. Hope this helps someone with the same situation.

First of all insert field in your code:
<input id="reloadValue" type="hidden" name="reloadValue" value="" />
then run jQuery:
jQuery(document).ready(function()
{
var d = new Date();
d = d.getTime();
if (jQuery('#reloadValue').val().length == 0)
{
jQuery('#reloadValue').val(d);
jQuery('body').show();
}
else
{
jQuery('#reloadValue').val('');
location.reload();
}
});

There are many ways to disable the bfcache. The easiest one is to set an 'unload' handler. I think it was a huge mistake to make 'unload' and 'beforeunload' handlers disable the bfcache, but that's what they did (if you want to have one of those handlers and still make the bfcache work, you can remove the beforeunload handler inside the beforeunload handler).
window.addEventListener('unload', function() {})
Read more here:
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/1.5/Using_Firefox_1.5_caching

Related

Determine if the user is reloading the current page in onunload

Is it possible to determine if the user is reloading the current page from within the events window.onunload or window.onbeforeunload?
I need to determine if the user is reloading the page and if they are stop my code from deleting a cookie.
window.onunload = window.onbeforeunload = function(e) {
// How can I detect a page refresh?
if (USER_NOT_REFRESHING)
$.removeCookie("my_session", { path: '/' });
else {
// user is refreshing
}
}
No, it's not possible. The browser only indicates the user is leaving, not why.
You can try using the PerformanceNavigation API
So, if the browser supports performance navigation, then you can check for reload by doing:
if(performance.navigation.type===1) {
// it's a reload
}
The above method may not work for code snippets

Document ready form submission and browser history

I have the following code in my page to submit the form on the page automatically when the DOM is ready:
$(function () {
$('form').submit();
});
However, on the next page if the user clicks back on their browser it goes back to the page before this one rather than the page with this code on (with Chrome/IE anyway). i.e. the page with the form on is missing in the browser history.
This is great, although I wondered is this something all modern browsers now do? I am looking for an answer that cites official sources such as from internet standards documents or from browser vendors that state the mechanism they have implemented.
This appears to only happen if I call the submit() function in the DOM ready or Window load events.
e.g. this code will show the form page in browser history after the page is clicked (back/forward):-
document.addEventListener('click', function () { document.forms[0].submit(); }, false);
the following snippets won't:-
document.addEventListener('DOMContentLoaded', function () { document.forms[0].submit(); }, false);
window.addEventListener('load', function() { document.forms[0].submit(); }, false);
window.onload = function () { document.forms[0].submit(); };
I've dealt with this before. I did not want the back button to take
the user back to previous page. Using onbeforeunload solved the
issue for me...
But your issue is related to the following concepts
Browsing Context
Session History
Replacement Enabled (flag)
A "Browsing Context" is an environment in which "Document" objects
are presented to the user.
The sequence of "Document"s in a "Browsing Context" is its "Session History". The
"Session History" lists these "Document"s as flat entries.
"Replacement Enabled" comes into effect when we propagate from one "Document" to another in the "Session History". If the traversal was initiated with "Replacement Enabled", the entry immediately before the specified entry (in the "Session History") is removed.
Note A tab or window in a Web browser typically contains a browsing context, as does an iframe or frames in a frameset.
Logically thinking, by calling any of these
document.addEventListener( 'DOMContentLoaded', function() {document.forms[0].submit();}, false );
window.addEventListener( 'load', function() {document.forms[0].submit();}, false );
window.onload = function() {document.forms[0].submit();};
you are suggesting the browser to perform #3, because what those calls mean
is that propagate away from the page as soon as it loads. Even to me that code is
obviously :) asking to be cleared off from the "Session History".
Further reading...
onbeforeunload
browsers
browsing-context
unloading-documents
replacement-enabled
Since this code leaves the page in the history when responding to the click event:-
document.addEventListener('click', function () { document.forms[0].submit(); }, false);
and the following pieces of code do not leave the page in history (DOMContentLoaded, and window onload events):-
document.addEventListener('DOMContentLoaded', function () { document.forms[0].submit(); }, false);
window.addEventListener('load', function() { document.forms[0].submit(); }, false);
window.onload = function () { document.forms[0].submit(); };
it can be assumed that modern browsers do not record a navigation history for page navigation that occurs within the window load or document ready handlers.
When the user hits the back button, the browser shows the cached copy of the page. Form submit doesn't cache the page therefore it doesn't show up in your history.
Yes, redirecting from an onload event handler causes the new URL to replace the one you leave in the history (and thus doesn't add a useless entry). But that's not the only trigger for that replacement, it may also be caused by any location change occurring fast enough, this delay being designed to avoid polluting the history in case of JavaScript based re-directions.
It is very hard to find any specification on that topic but on Firefox this delay seems to be 15 seconds. Here's a mention of this delay in bugzilla from one of the moz developers :
Mozilla uses a threshold of 15 seconds to decide if a page should
stay in history or not. If a site uses and
redirects to another site with in 15 seconds OR redirects to another
page in onLoadHandler() etc ..., the redirected page will replace
(and thereby eliminating) the redirecting page from history. If the
redirection happens after 15 seconds, the redirecting page stays in
history.
One may argue about the
time limit. But this is just something we thought was a reasonable number

JavaScript - bfcache/pageshow event - event.persisted always set to false?

In a standard Java / SpringMVC / JSP / jQuery web-app, I'm trying to detect a "Back" (or history.go(-1)) event, in order to refresh (AJAX) a summary component/panel content when I return to a page (where we can change the backend data that is displayed by the summary component).
I tried the following in JavaScript (following some posts on StackExchange re how to achieve this):
<script type="text/javascript">
$(document).ready(function() {
window.onpageshow = function(event) {
console.log("Event:");
console.dir(event);
if (event.persisted) {
alert("non-jQuery - back to page - loaded from bfcache");
} else {
alert("non-jQuery - loaded page from server");
}
};
$(window).on("pageshow", function(event){
console.log("Event:");
console.dir(event);
if (event.originalEvent.persisted) {
alert("jquery - back to page - loaded from bfcache");
} else {
alert("jquery - loaded page from server");
}
});
});
</script>
I am running OpenSUSE Linux and have tried this with FireFox and Chrome (latest versions), but every time the event's persisted attribute is set to false (I can see this in the JavaScript console and by the alerts that pop-up from the above code). By every time, I mean, regardless of whether it was loaded from the server or shown again via the Back button (or a 'Back' link).
My intention was to make an AJAX call to reload the summary component/panel with the updated data from the server if the page was showing via the Back button or history.go(-1) call.
I also tried setting an unload handler (that does nothing) to prevent the page from being put into the bfcache but it still seems to be showing a bf-cached version and the event.persisted (or event.originalEvent.persisted) is set to false.
Is this property managed correctly on Linux? Am I doing something stupid in my code? Any help or ideas would be much appreciated, thanks!
I have found hidden input buttons are not a reliable solution since they may hold the wrong value when the user navigates back to the page and then hits refresh. Some browsers (Firefox) retain input values on refresh so every time the user hits refresh it will refresh again since the input button holds the wrong value. This is a typical scenario for forums (user views a topic, hits the back button to go back to the list of topics, and may continue to hit refresh to check if there are new topics).
As noted by Grégoire Clermont, event.persisted is buggy in chrome (and IE) and this still hasn't been fixed for either browser as of Feb 2017. The good news is you can rely on window.performance.navigation.type == 2 for chrome and IE. Ironically Firefox is unreliable for the latter but it shouldn't matter since it is reliable for event.persisted. The following code worked for me:
if (document.addEventListener) {
window.addEventListener('pageshow', function (event) {
if (event.persisted || window.performance &&
window.performance.navigation.type == 2)
{
location.reload();
}
},
false);
}
Update 2022:
Because window.performance.navigation.type is deprecated (ref: MDN), I updated the code to do the same thing:
if (document.addEventListener) {
window.addEventListener('pageshow', function (event) {
if (event.persisted || performance.getEntriesByType("navigation")[0].type === 'back_forward') {
location.reload();
}
},
false);
}
This appears to be a bug in Chrome (also present in IE11).
I have found the following workaround:
<input type="hidden" id="cacheTest"></input>
<script>
var input = document.querySelector('#cacheTest')
if (input.value === "") {
// the page has been loaded from the server,
// equivalent of persisted == false
}
else {
// the page has been loaded from the cache,
// equivalent of persisted == true
}
// change the input value so that we can detect
// if the page is reloaded from cache later
input.value = "some value"
</script>
This exploits the fact that in most browsers, when the page is loaded from the cache, form fields values are also conserved.
I know this is a bit late but this works for me:
window.onpageshow = function(e) {
if (e.persisted) {
alert("Page shown");
window.location.reload();
}
};
I don't think you need it in the document ready function, just use vanilla as above.

Frame Busting buster not completely working for IE

I've been working on a Frame busting buster (what's in a name, hehe), which kept my users on my page and open a new window with the target URL. I'm using a Lightbox script to display iframes, this is what I'm doing:
1) Added an event for all .lightbox clicks, f.e:
$('.lightbox').live("click", function(e) {
e.preventDefault();
$('#redirectURL').val($(this).attr('href'));
$(this).lightbox();
}
2) Added a frame busting buster:
<script type="text/javascript">
var prevent_bust = 0
window.onbeforeunload = function() { prevent_bust++ }
setInterval(function() {
if (prevent_bust > 0) {
prevent_bust -= 2
window.top.location = 'http://server-which-responds-with-204.com'
}
}, 1)
</script>
3) Modified the frame busting buster code to fit my needs, which are:
detect if an iframe wants to change the window.top.location
if so, prevent this from happening using the 204 server respond
open a new page: window.open( $('#redirectURL', '_blank' );
close lightbox: $('.jquery-lightbox-button-close').click();
So far, this is what I've come up with:
var prevent_bust = 0
window.onbeforeunload = function() { prevent_bust++ }
setInterval(function() {
if (prevent_bust > 0) {
prevent_bust -= 2;
redirectURL = $('#redirectURL').val();
if(redirectURL != "") {
window.top.location = 'http://www.****.com/ajax/nocontent.php';
window.open(redirectURL, "_blank");
$('.jquery-lightbox-button-close').click();
$('#redirectURL').val('');
} else {
window.top.location = 'http://www.****.com/ajax/nocontent.php';
}
}
}, 1);
// EDIT: Before I forget, 'nocontent.php' is a file that returns a 204 header
For Firefox it acts as I programmed it, if there's a change detected in the window.top.location it opens a new frame/page and prevents the iframe from reloading the top location and to round it up, it closes the jQuery lightbox.
Safari/Chrome act similar, they open a new browser screen (not sure if theres an option to say target="_newtab" or something?). Only bad thing is they do not really display a message of the popup is blocked, but I can work around that by displaying a popup balloon on my website with a link to the page.
Internet Explorer is, what a shocker, the only black sheep left.. IE does not open a new popup, nor blocks the window.top.location reset by the iFrame and simply continues refreshing the complete page to the '#targetURL'. It does the same with the default busting code.. so it's not because of some of my edits.
Anyone who is able to spot a mistake in my code?
Also, I would need a little modification that sees if the request has been made by an iframe or by the user itself, because now there is really NO option for a user to leave my page by changing the address in the toolbar or by clicking a link, which is not really needed LOL.
Thanks in advance.
PENDO, I tried to simulate the whole process you described, ligthbox-jquery, javascript their own codes and controls opening pages via lightbox. I could not simulate at all, and as time is running out I'm sending a suggestion to broaden the range of possibilities and solutions.
I suggest replacing the redirect page:
...
redirectUrl = $ ('# redirectUrl'). val ();
...
window.top.location = 'http://www .****. with / ajax / nocontent.php';
window.open (redirectUrl, "_blank");
Replaced with a DIV container that simulates a page, using ajax calls and taking the content and overwritten the contents of the DIV.
...
$.post(redirectoURL /* or desired URL */, function(data) {
$('DIV.simulateContent').html(data);
});
...
or
...
$('DIV.simulateContent').load(redirectoURL);
...
This approach also avoids the problem of preventing the user from even leaving your page using the address bar (as you yourself mentioned).
Sorry, let me give you a complete solution, but time prevented me.
PENDO, a little more work on alternatives to the problem, I found a customizable jQuery lightbox plugin for working with custom windows yet (iframe, html, inline ajax etc.). Maybe it will help. The following link:
http://jacklmoore.com/colorbox/
If you don't need javascript running in your iframe in IE, you can set the iframe security attribute :
<iframe security="restricted" src="http://domain.com" />
http://msdn.microsoft.com/en-us/library/ms534622(v=VS.85).aspx

After travelling back in Firefox history, JavaScript won't run

When I use the back button on Firefox to reach a previously visited page, scripts on that page won't run again.
Is there any fix/workaround to have the scripts execute again when viewing the page the second time?
Please note that I have tested the same pages on Google Chrome and Internet Explorer and they work as intended.
Here are the files and the steps I used to test the problem:
(navigate to 0.html, click to get to 1.html, back button)
0.html
<html><body>
<script>
window.onload = function() { alert('window.onload alert'); };
alert('inline alert');
</script>
Click Me!
</body></html>
1.html
<html><body>
<p>Go BACK!</p>
</body></html>
Set an empty function to be called on window.onunload:
window.onunload = function(){};
e.g.
<html><body>
<script type="text/javascript">
window.onload = function() { alert('window.onload alert'); };
window.onunload = function(){};
alert('inline alert');
</script>
Click Me!
</body></html>
Source:
http://www.firefoxanswer.com/firefox/672-firefoxanswer.html (Archived Version)
When I use the back button on Firefox to reach a previously visited page, scripts on that page won't run again.
That's correct and that's a good thing.
When you hit a link in Firefox (and Safari, and Opera), it does not immediately destroy your page to go onto the next one. It keeps the page intact, merely hiding it from view. Should you hit the back button, it will then bring the old page back into view, without having to load the document again; this is much faster, resulting in smoother back/forward page transitions for the user.
This feature is called the bfcache.
Any content you added to the page during the user's previous load and use of it will still be there. Any event handlers you attached to page elements will still be attached. Any timeouts/intervals you set will still be active. So there's rarely any reason you need to know that you have been hidden and re-shown. It would be wrong to call onload or inline script code again, because any binding and content generation you did in that function would be executing a second time over the same content, with potentially disastrous results. (eg. document.write in inline script would totally destroy the page.)
The reason writing to window.onunload has an effect is that the browsers that implement bfcache have decided that — for compatibility with pages that really do need to know when they're being discarded — any page that declares an interest in knowing when onunload occurs will cause the bfcache to be disabled. That page will be loaded fresh when you go back to it, instead of fetched from the bfcache.
So if you set window.onunload= function() {};, what you're actually doing is deliberately breaking the bfcache. This will result in your pages being slow to navigate, and should not be used except as a last resort.
If you do need to know when the user leaves or comes back to your page, without messing up the bfcache, you can trap the onpageshow and onpagehide events instead:
window.onload=window.onpageshow= function() {
alert('Hello!');
};
You can check the persisted property of the pageshow event. It is set to false on initial page load. When page is loaded from cache it is set to true.
window.onpageshow = function(event) {
if (event.persisted) {
alert("From bfcache");
}
};
For some reason jQuery does not have this property in the event. You can find it from original event though.
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
alert("From bfcache");
}
});
In my case window.onunload with an empty function didn't help (I tried to set a value for dropdown when user uses backwards button). And window.onload didn't work for other reason - it was overridden by <body onload="...">.
So I tried this using jQuery and it worked like a charm:
$(window).on('pageshow', function() { alert("I'm happy"); });
Wire in an "onunload" event that does nothing:
<html><body>
<script type="text/javascript">
window.onload = function() { alert('window.onload alert'); };
window.onunload = function(){};
alert('inline alert');
</script>
Click Me!
</body></html>
As far as i know Firefox does not fire onLoad event on back.
It should trigger onFocus instead based from this link here.
A simple way to cause a page to execute JavaScript when the user navigates back to it using browser history is the OnPopState event. We use this to pause and replay the video on our home page (https://fynydd.com).
window.onpopstate = function() {
// Do stuff here...
};
for some cases like ajax operations url change listener can be used
$(window).on('hashchange', function() {
....
});

Categories