I'm using pushState and generating the page view on popstate events based on the history.state of the current page.
If there is no history.state I (hope) to reload the document.
window.onpopstate = function(event) {
if( window.history.state !== null ){
// page was ajax created and history created by pushState
// recreate with ajax
}
else{
// page was loaded from server normally
document.location.reload()
}
}
At issue is Safari fires a popstate event on the initial page load.
Chrome and Firefox fire popstate on the back/forward browser button.
I want to ignore the initial load popstate event on Safari (ideally without setTimeout and without browser detection).
Additionally, the site is a mix of links - some that will trigger pushState and some that will load normally from the server - so if a user is ten pages into navigation history and clicking the back button, the history will have a mix of pushState pages and non-pushState pages.
I have solved the problem saving the initial order value obtained from window.history.state.order in a variable. When the on popstate event is triggered, the value previously stored is compared to the current order value. It they are the same then nothing is needed to be done (you have to ignore the initial popstate value triggered by Safari). Here you can see and example:
var initial_oder = window.history.state.order || 0;
window.onpopstate = function(event) {
if( window.history.state !== null && window.history.state.order !== initial_order){
// page was ajax created and history created by pushState
// recreate with ajax
}
else{
// page was loaded from server normally
document.location.reload()
}
}
Hope this help!
Related
I have a small application that opens a new popup. I store the window in a variable:
popup = window.open("sites/display.html", "_blank");
After that I add a beforeunload Eventlistener:
$(popup).on('beforeunload', function(){
// Do something
});
I then later reload the window with a button:
popup.location = popup.location;
After that if I close the window the Event beforeunload isn't fired anymore. I think it has something to do with the reload because if I dont reload the page everything works fine.
How can I fix this so the event is fired everytime the window closes?
Exact code I use:
function startClock(allowRestart) {
saveSettings(allowRestart);
if ($("#separated-display").is(":checked")) {
// Separated mode activated
if (popup == undefined) {
openPopup();
} else {
if (popup.closed) {
openPopup();
}else{
popup.location.reload();
}
}
} else {
// Separated mode deactivated
if (popup != null && popup.closed == false) {
$(popup).unbind();
popup.close();
}
window.location = "sites/display.html"; // Open the clock in same window
}
}
function openPopup(){
// Open new popup window
popup = window.open("sites/display.html", "_blank");
// TO-DO: Fix event not fired after 1. window reload 2. window close
popup.addEventListener('beforeunload', function(){
console.log("unload");
});
}
As you're trying to access the same origin (with the relative path) window using window.open(), Access error shouldn't be displayed.
popup = window.open("/sites/display.html", "_blank")
popup variable would refer to the newly created window which is a thin wrapper representing a WindowProxy object, which indeed has all features of window available.
When the page reloads everything is set to default, and the window loses its properties set before. Therefore, the unload event attached earlier is not anymore attached. This would happen for other events as well.
Hence the problem here that the event is being attached to the popup window just once on opening the popup, which is reset on page reload. The best way to go forward would be to add the unload event in the js file which loads specifically on sites/display.html page. There, every time when sites/display.html page loads you could access the new window object and attach events in window.load / document.ready (according to your use case).
You won't able to attach the event to pop up before or after invoking page reload as you're doing it currently as, the property would be reset if you try setting it before/after page reload as it might be executed asynchronously.
NOTE:
You should rather use the reload() function exposed by window.location instead of updating the location property. As updating the property doesn't skip browser cache.
popup.location.reload()
The support for window.open is unknown for most browsers, though I was able to use it on Chrome 84.
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
The iOS Safari doesnt't seem to trigger pageshow event in the following situation.
Lets say I have 3 pages
Page A : (has some code code on pageshow event)
Page B
Page C
User navigates from A -> B. Presses the back button. (pageshow triggers fine)
User then navigates to another page could be Page B or Page C. Then presses the back button again. (pageshow doesn't trigger)
On the contrary if the user minimizes and maximizes the window again or switches to another window and back (by pressing the middle button on iPhone) the pageshow event is triggered again.
Everything seems to work fine on Android
window.onpageshow = function(e) {
alert('hello');
}
Did anyone else face it? I spent hours on this thing and couldn't think of a workaround.
Any help would be greatly appreciated.
Hack : This is what worked for me
var myCustomEvent = (navigator.userAgent.match('iPhone') != null) ? 'popstate' : 'pageshow';
$(window).on(myCustomEvent, function(e) {
...
}
For some reason popstate triggers everytime when page state changes in iOS but not in Android.
Try using:
window.onpageshow = function(event) {
if (!event.persisted) {
alert("hello");
}
};
Persisted is false on initial page load, so you can check against it, and if it false, it is your first page load.
The popstate event doesn't seem to work any more, at least for me. I worked out some third-party script on my page was breaking this, but wasn't able to work out which one. I came up with this hack:
addEventListener('pageshow', () => {
history.replaceState({}, document.title, window.location.pathname);
// called on initial load and first back
});
addEventListener('popstate', () => {
// called on all back events
});
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
I have a flash app, that redirects to another page.
I'd love to trap any other window unload event (clicking links / submitting forms) and warn the user they'll lose their progress in the Flash app.
However, I can't find any way to tell that the click/change of URL/window unload was triggered by Flash vs being triggered by a normal link etc.
Is there any way to detect this, and how reliable is it?
There's no way to tell what caused an unload event directly in the event handler. However, you might be able to achieve this by attaching an onclick event handler to every link on the page that sets a variable. In jQuery:
$("A").click(function(){window.clickedLink = this});
You might read the clickedLink in your unload event and tell the difference.
How do you redirect to the other page from Flash?
What you can do is redirect with a Javascript-function, and call this from Flash (with ExternalInterface). When you call this redirect, you set a certain flag, indicating you're redirecting from Flash. Then set a listener for the window unload event, and check if the flag is set. If not, you can show the message to the user. Otherwise, just skip this and redirect.
<script>
var callFromFlash = false;
window.unload = unloadPage;
//call this function from Flash using ExternalInterface
function doRedirect(url)
{
callFromFlash = true;
//redirecting
window.location.href = url;
}
function unloadPage()
{
if(!callFromFlash)
{
//show message and wait for response
}
}
</script>