catching beforeunload confirmation canceled? - javascript

I want to do some stuff when user is leaving a page, I add this code
window.onbeforunload = function (e){
return "You save some unsaved data, Do you want to leave?";
}
This prompt can notify the user and user can stay on the page or leave. But I want more to know whether he leaves or not, and do thing on his decision. I tried this,
window.onbeforunload = function (e){
var event = jQuery.Event(e);
var result = confirm('want to leave?');
if (result == false){
//do sth..
event.preventDefault();
}else{
//do clean up
}
}
But it fails!! It always goes away!
Can any body help me doing this?

The method you use (preventing bubbling of the event) is intentionally not possible, otherwise you could prevent users from leaving your page.
You can achieve something similar to what you want by doing your cleanup onunload, and do the stuff you always want to do onbeforeunload.

But I want more to know whether he leaves or not, and do thing on his decision
If you wanna do something when he leaves, you can do it in unload event. For example, as #Erik Bakker mentioned you can send async events in unload event.
However if you wanna find out if user "stayed", in other words cancelled the leaving process, there is a way as well. It's kinda hackish, but it works.
const doSomethingWhenUserStays = function doSomethingWhenUserStays() {
alert('user stayed!!!');
}
window.addEventListener('beforeunload', function onBeforeUnload(e) {
setTimeout(doSomethingWhenUserStays, 500);
// Dialog text doesn't really work in Chrome.
const dialogText = 'A dialog text when leaving the page';
e.returnValue = dialogText;
return dialogText;
});
Method doSomethingWhenUserStays will be called every time, but if user leaves the page, he won't see what it performed anyway. It can perform asynchronous stuff, synchronous, it doesn't really matter because it's within setTimeout therefore it's out of the normal flow of onBeforeUnload and won't interfere with it.
If you want to perform it ONLY if user really stays on the page it's slightly harder. You'd have to set a global flag that checks whether user reached unload or not and only then call what's inside doSomethingWhenUserStays. Consider the following example.
let hasUserLeft = false;
const doSomethingWhenUserStays = function doSomethingWhenUserStays() {
// Perform the following only if user hasn't left the page
if (!hasUserLeft) {
alert('user stayed!!!');
}
}
window.addEventListener('beforeunload', function onBeforeUnload(e) {
// It won't perform doSomethingWhenUserStays in 500ms right after this is called,
// but instead, it will perform it in 500ms after you click "Stay" or "Leave".
// Therefore, there should be some time for `unload` handler to fire and
// set `hasUserLeft` flag before `doSomethingWhenUserStays` is called.
setTimeout(doSomethingWhenUserStays, 500);
// Dialog text doesn't really work in Chrome.
const dialogText = 'A dialog text when leaving the page';
e.returnValue = dialogText;
return dialogText;
});
window.addEventListener('unload', function onUnload() {
hasUserLeft = true;
});

As far as I have read about this method in different browser forums like MSDN, MozillaDev, etc, this method does not have any callbacks for OK/Cancel. You have this for the confirm dialog but not for this.
This is a security implementation to allow users to have full right about which website they should see. Also, it averts hackers from locking users to their sites.

Related

Unexpected window.confirm behaviour while hooking into angular-ui-router $transitions API

I've got this listener setup on a form of mine that checks for a state transition to occur via angular router. When the listener is tripped it checks if the form is dirty, if it is it throws a window.confirm alert up saying the user may have unsaved changes.
All of that looks like this
this.setListener('form.dirty-check', this.setExitCheck);
setListener = (el, cb) => {
if ($(el).length) {
cb();
} else {
setTimeout(() => {
this.setListener(el, cb);
}, 500);
}
};
setExitCheck = () => {
this.$transitions.onStart({}, () => {
if ($('#compForm').hasClass('ng-dirty')) {
if (window.confirm('You may have unsaved changes! Press ok to continue, or press cancel to go back and save your work.') === false) {
return false;
} else {
return true;
}
}
});
};
This code is working pretty well, except for a singular bit of unexpected behaviour.
For some reason, when I hit, "Ok" to leave the page the transition will fire off just fine, but if I go back to the page and try it again, I now have to hit okay twice, and get two window.confirm alerts. If I go back a third time to try, I get three window.confirm alerts, and have to hit Ok on all three of them. I tried this up to the point of receiving 10 alerts, and have to press ok 10 times.
Once I refresh the page though, it seems to reset, and I start it all over again. Works right away, then takes two Ok's, then three, and so on.
Does anyone know what might be going on causing this incremental behaviour?
ui-router won't clear listeners automatically, so you have to clear it manually.
and $transitions.onStart returns a function which will destroy the listener's hook when it's called. documentation here(the last line).
the syntax is the same as deregister events of $rootScope, refer How can I unregister a broadcast event to rootscope in AngularJS?
$scope.onStartHandler = this.$transitions.onStart(...);
$scope.$on('destroy', function() {
$scope.onStartHandler();
});

Skip beforeunload handler if user stays on page due to dialog within second handler

I have one beforeunload handler, that is called when the user actually navigates away from the page:
$(window).on("beforeunload", function () {
cleanup();
});
Another part of my application might however add another beforeunload handler, that asks the user if he really wants to leave because of an ongoing operation:
$(window).on("beforeunload", function () {
return "Do you really want to leave";
});
How can I assure, that the second handler will always be called first and that the first handler will not be called in case the user decides to stay on the page?
I already tried to use the unload event instead. But this doesn't work since it will not execute my clean-up function and the backend call within that function reliably.
You can use window.confirm in case. As in (pseudo-code)
$(window).on("beforeunload", function () {
var result = window.confirm('Do you really want to quit?');
if (result) {
cleanup();
}
}
This way, if the user wants to quit it will cleanup. Does that work?>

If all ajax are not done and user leaves the window or page

I have created a script that starts after all ajax is done. I am trying to modify the script and add a condition like if ajax is not done AND user left the windows then trigger function. Is this possible?
var isDone=false;
$(document).ajaxStop(function() {
if(isDone) {
return;
} else {
isDone=true;
$.post("lib/ajax.html", { action: 'update_date', domain: domain });
}
//if(isDone == false && ) if ajax is not done and user leaves page
// exits then post to update date
});
To check if a user leaves a page, use the following function and within that check the isDone flag:
$(window).unload(function(){
if(!isDone) {
<<execute function>>
}
});
Let me know if this works.
You can use $.active to check if any ajax request is pending right now or not. And then use
$(window).unload(function(){
if ($.active > 0) {
//Do something
}
});
Instead of using th unload-function of jQuery (which is deprecated in version 1.8) I would recommend using the native javscript here:
window.onbeforeunload = function(){
if(!isDone) {
//do something or if you want the user to confirm that he want to leave:
return "You have attempted to leave this page. Are you sure?";
}
}
onbeforeunload
Note: This function is no part of any standard so there can be different behaviours in different browser and versions.
If it is important to post that information, I would let the server script post its status to the update_date script instead.
The server is likely to finish the request, even when the user closes the browser, and that way you don't depend on the user/browser.
People will be able to close their browser. They could even kill the browser process if you try to block them.

Is it a bad idea to trigger a database lookup on keyup (i.e. checking for unique username)

I have a field where a user can manually enter a code for a new product. It cannot be a db-generated ID as the user needs control over the code.
As this will require a round-trip to the server to check, my intention was to trigger this check as the "keyup" event happens (a bit like a live filter would work) so that there is instantaneous feedback about the entered code (and probably CSS-based colour/image reinforcement). Admittedly for this particular function I may choose to do it onblur instead of keyup but there may well be other situations where I require a check on keyup.
Is this fundamentally a bad thing or not? A round-trip to the server to check if an item code exists (primary key) should be a very fast process but with a slow connection I'm just wondering if there could be a race condition whereby a fast typer stacks up the db calls faster than it can return and then a situation may emerge whereby a "submit" button is enabled when it shouldn't be. Of course I would also back this up with db-level checking on submission of the form, but I'm trying to make this as apparently responsive as possible.
Is it generally a bad idea to do keyup checking on any data source that's not held in memory or is this an acceptable practise?
Since you use knockout you can use throttle
ViewModel = function() {
this.text = ko.observable().extend({ throttle: 500 });
this.text.subscribe(this.onText, this);
};
ViewModel.prototype = {
onText: function(value) {
console.log("ajax call");
}
};
http://jsfiddle.net/TT3AB/
There's never going to be a race condition if you abort any previous ajax request on the next keyup.
EDIT
(function() {
var xhr; //a reference to an XMLHttpRequest Object
var onKeyUpCallback = function() {
if(xhr) {
xhr.abort();
}
xhr = ... //build your XHR object
//....
xhr.send();
}
yourInputElement.addEventListener('keyup',onkeyUpCallback);
}());
If you want to check at real time, you can approach in this way. When user types data and halts for a while then send you ajax request to check for existence of that particular thing in database.
Call a function on key up event but do not send request until user stops for a while
var timer;
function onkeyups()
{
if(timer)
{
clearTimeout(timer);
}
timer = setTimeout(function(){callRequest();},500) // the delay, after how much millisecond the ajax call should be made when user has stopped typing
}
function callRequest()
{
// Make your ajax call or whatever
}

Need something earlier than a page unload?

I have set up an unload listener which sets a flag that error handlers from an ajax request check.
jQuery(window).unload(function() {
unloadhappening = true;
});
However, the ajax request can be aborted (when the user navigates to another page) and the error handler for the ajax request invoked before the unload event is fired.
I was wondering could I get an event earlier than unload? Obviously I could put a listener on every link to move from the page but was looking for a neater way if there is one.
Thanks
You could probably use onbeforeunload or $(window).on('beforeunload') but return an empty string from the function to prevent the prompt about navigation.
window.onbeforeunload = function(e) {
unloadhappening = true;
// maybe other logic
return ''; // or maybe return null
}
I haven't tested the solution to avoid the popup box in all browsers, so your milage may vary.

Categories