Is there such a thing?
I know that I can hook my function on the click event of all links, but there are other situations where a page is changed, like refresh or when a different script changes the window.location
In the end, I did it by sending a string trough postMessage from the unload event, like this:
$(window).bind('unload', function(e){
window.parent.postMessage('unloading');
});
in the parent document:
$(window).bind('message', function(e){
if(e.originalEvent.data == 'unloading'){
// ajax stuff here
}
});
It appears to work.
I probably should have mentioned that there's a iframe involved :)
There's the beforeunload event, which is fired when the page is being torn down (either to follow a link, or if the window is being closed, or refresh, etc.). Example:
window.onbeforeunload = function(event) {
var s = "You have unsaved changes. Really leave?";
event = event || window.event;
if (event) {
// This is for IE
event.returnValue = s;
}
// This is for all other browsers
return s;
}
There are, for obvious reasons, very strict limits on what you can do in the handler of the beforeunload event, and as you can see above beforeunload handlers have a different signature than normal event handlers. Basically, your code can't do anything asynchronous, can't open new windows, and can't cancel the event. It can return a string, and if it does, the browser will pop up a window asking whether you really want to leave the page, and including your string in that pop-up.
From your comment on the question:
I need it before so I can fire a ajax request and update some things...
The way to do that here many years after the question was originally asked is with the beacon API. This lets you send a non-blocking asynchronous request to the server without slowing down the process of the browser tearing down your page and navigating to the next:
navigator.sendBeacon("/path/to/notify", optionalData);
It's a send-and-forget, but the browser doesn't cancel it when your page is torn down (like it does a standard asynchronous ajax request). Instead, it allows that request to complete even though your page has been removed.
Back in 2012 when this answer was originally written, you could usually get away with a synchronous ajax call (async: false) provided it didn't take too long. But you can't reliably do that now (and it was never a good idea, it holds up the UI).
jQuery has unload function:
The unload event is sent to the window element when the user navigates
away from the page. This could mean one of many things. The user could
have clicked on a link to leave the page, or typed in a new URL in the
address bar. The forward and back buttons will trigger the event.
Closing the browser window will cause the event to be triggered. Even
a page reload will first create an unload event.
Note that this should be binded to window object instead of document:
$(window).unload(function() {
// do something
});
You can also bind handler to beforeunload event:
$(window).bind('beforeunload', function() {
// do something, preferably ajax request etc
return 'are you sure?';
});
When a page is reloaded, whatever was there before will be gone. Thus, it seems like what you're talking about is something you'd do at DOMReady or "load" in the new page, since you can't "push" code from the former page into the new context.
Related
I'm trying to use "pushState" and the "popstate" event to trap Back button navigation, however, while the popstate event triggers correctly in Firefox, it doesn't trigger in Chrome (Version 76.0.3809.87 (Official Build) (64-bit)) if there is no user interaction.
From testing, it looks like the popstate event only gets triggered if the user interacts with the page (ie. clicks somewhere on the document). So if you load the page without interacting and hit Back, the popstate function is not called.
I've added a Fiddle to showcase this: https://jsfiddle.net/0xwvLndu/
To test the Fiddle in Chrome, just click the link and hit the Back button. You'll see no alert. Then click the link again but this time click anywhere on the Fiddle document and then hit the Back button, the alert is then triggered.
I found a discussion on the Chromium forum that may relate to this quirk, and perhaps this has been implemented to prevent abuse of history entries - https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/OCS7g72HtyI and https://github.com/WICG/interventions/issues/21#issuecomment-425609246
If this is the case, does it mean that popstate cannot be relied on anymore to trap Back button actions, and if so, is there a work around solution?
Below is an example of what I've been testing with:
window.addEventListener('load', function() {
history.pushState(null, null, document.URL);
});
window.addEventListener('popstate', function(event) {
alert('test');
});
I expected the alert to be triggered on Back Button regardless of user interaction, but this does not happen in Chrome.
Try adding a setTimeout of 0
window.onpopstate = () => setTimeout(alert.bind(window, "Pop"), 0);
When writing functions that process popstate event it is important to take into account that properties like window.location will already reflect the state change (if it affected the current URL), but document might still not. If the goal is to catch the moment when the new document state is already fully in place, a zero-delay setTimeout() method call should be used to effectively put its inner callback function that does the processing at the end of the browser event loop: window.onpopstate = () => setTimeout(doSomeThing, 0);
Content is taken from
https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event
On a sidenote it is advised not to use this as browsers may remove this any time.
As others suggested in comment, this should be by design of browser software to prevent hijacking of its default back button behavior.
I have an HTML page that includes only a script tag, I don't control the script and I can't change it (so I can't fire my custom event for example).
The script ends with a redirect (using window.location).
Is there a way to add a new script to the page that will listen to the page events and "catch" the redirect (actually it's better for me to catch the new loaded document)?
Something like:
window.addEventListener('redirected', function() {
// do staff
});
(I know there is no "redirected" event, it's just for the example).
It's very important to make it clear that the redirect isn't caused by an anchor click or back/forward button click, so I can't use events like click or popstate.
You might want to look at the onpagehide event or the onunload event, both of which occur when the user navigates away from the page.
However, if you wish to interfere or prevent the redirection itself, onbeforeunload is what you want.
Just take a look at :
unload function w3school or mozilla developper network
beacon function for sending a final XMLHttpRequest
I have an onunload method written on the body of a form. Since the page got large amount contents to be streamed from the server,it takes a while to download the entire form. While the form gets downloaded to the browser,if the user clicks the window close button, the unload event (written to call a server side action to reset some flag) is not getting triggered sometimes. The unload method is written towards the end of the page, is that a reason for this? Is there anyway by which we can make sure that onunload is guaranteed to be called?
The thing that hits you is probably the fact that IE doesn't fire an unload event if window.onload hasn't fired yet. The solution is described in this blog post: http://blog.moxiecode.com/2008/04/08/unload-event-never-fires-in-ie/
There are two reasons. One, like you said, the browser may not even be reaching the unload, so you should declare it above your body tag.
The other is that, as GoldenNewby already stated, you need to give the code some time to finish, such as an alert. This is because the JavaScript is cleared from memory the moment the next page is ready, and execution is simply stopped on the old script. If you can guarantee the script will take less time than a page load, you won't need an alert, but if it could take more than 2ms or so to execute, you will need to buy some time.
IE has an onbeforeunload event, you can use that instead. Just set a flag so that your onunload can exit early if onbeforeunload already did its thing.
window.onunload = window.onbeforeunload = function () {
if (window.isAlreadyUnloaded) return;
window.isAlreadyUnloaded = true;
// do your stuff here.
}
Have been stuck with this issue for a few days now, and really need, and would appreciate some help. My requirement is that I want to make a server side callback to clear off some objects when the user navigates away from our page, without clicking logout. For business reasons, our ASP.NET session timeout has to be set to a very high value. Further, I do not want to popup a alert/dialog to force the user to return to the page and click Logoff.
The solution I have arrived at thus far is to make a AJAX callback by embedding this javascript in the page.
window.onunload = pageleave;
function pageleave() {
alert('test');
PageMethods.CheckLogout('abc','xyz',OnSucceed,OnFail);
}
Here is the problem though :
For IE and Firefox, the unload fires, the alert is seen, and I see the callback on my C# side in all the cases I desire
a) User closes browser
b) User types in a new URL in the address bar
c) User clicks on a link causing page to reload
For Chrome and Safari, cases a and b work fine. However, when the user clicks on a link, which causes my aspx page to reload, my C# side code is not invoked. The javasacript alert is fired though.
I am trying to see how I can get Chrome/Safari to behave like IE/Firefox. Is this even a possibility?
Thanks in advance for the help,
Rajesh.
Use the beforeunload event instead of the unload event.
Also, use a synchronous AJAX request, not an asynchronous one, so that the request is completed before your beforeunload function exits.
I'm not sure how you would do a synchronous AJAX request using your JavaScript framework. In jQuery, it would look like this:
window.onbeforeunload = function() {
jQuery.ajax({
url: '/page/to/load',
async: false
});
};
In case anyone comes across this in the future, I couldn't use window.onbeforeunload as it was firing too early. Instead, I found window.onpagehide as a suitable workaround.
e.g.
window.onpagehide = function() {
// some code here.
};
I am trying to modify the content in my page without a reload. Currently my code reads:
window.onpopstate = function(event){
// Ajax Request the Page and replace content with new content
};
This works when I push a state then trigger the popstate event, but if I press the back button in the browser it navigates to the url instead of calling my onpopstate event. How can I prevent a page refresh and update the page with my ajax call instead?
edit: I am trying to update with pushState and popstate. I was hoping to keep my urls hash free.
You have to make sure there is always a history state you've pushed from the current page on the history to prevent the back button from performing a page load.
If you're trying to keep the user "contained" in your web app so the back button always provides some kind of function, you need to push at least two states onto the stack and then make sure to push another state from your popstate handler.
var foo = {foo: true}; // state object
history.pushState(foo, "unused argument", "#newInitialUri");
...
var bar = {bar: true}
history.pushState(bar, "unused argument", "#newStateOfWebApp");
...
window.onpopstate = function(event){
...
var baz = {baz: true}
history.pushState(baz, "unused argument", "#baseState");
};
In the above example say we loaded '/'. The script starts executing and the browser window URI changes to '/#newInitialUri' but no page load occurs. Then immediately after, the browser URI changes to '/#newStateOfWebApp' and no page load occurs.
The user pushes the back button on their browser. Your popstate handler fires. During your handler, event.state equals foo and the browser uri is '/#newInitialUri'. No page load occurs. The handler finishes completing, calling history.pushState and now the browser uri is '/#baseState'. No page load occurs. If the user clicks back again, your popstate event will fire again, event.state will equal foo (again), the browser uri will be '/#newInitialUri' (no page load) and then it will be '/#baseState' again (no page load).
The important thing to remember is that the event.state in your popstate handler always contains the state object for the URI you've just come back to, not the one you just came from. This was confusing to me at first, but made sense when I thought about it. For example, the user may have just come back to your page after perhaps having gone off to Google. The state object is your opportunity to communicate the status of your app to your code.
Keep in mind that some browsers fire the popstate event on page load (which is what's supposed to happen according to the spec from my understanding). Be sure to check for your state object in your handler before executing your code to make sure you don't run code you don't intend to on a page load.
One final note: if you're using jQuery to handle events, you'll need to use event.originalEvent.state to refer to the state object.
This may help
The unload event is sent to the window element when the user navigates away from the page. This could mean one of many things. The user could have clicked on a link to leave the page, or typed in a new URL in the address bar. The forward and back buttons will trigger the event. Closing the browser window will cause the event to be triggered. Even a page reload will first create an unload event.
Reference
http://api.jquery.com/unload/
untested
$(window).unload(function(e){
e.preventDefault();
$(window).trigger('popstate ');
});
$(window).bind('popstate ',function(){
//your ajax call here
});
and finally here is a DEMO click on browser's back button to see it working
update
you are right the unload be canceled but you can do some thing like
$(window).unload(function(e){
e.preventDefault();
$(window).trigger('beforeunload');
});
$(window).bind('beforeunload',function(){
alert('call your ajax here');
return '';
});
yet another DEMO