JS onbeforeunload runs unconditionally to user's selected choice - javascript

I have a very simple code:
window.onbeforeunload = function(e) {
console.log("closing");
e.preventDefault();
try { self.recorder.stop(); }
catch (err) { console.log(err); }
return "custom message here";
}
What I need is to run the recorder.stop() only if the user presses yes, and actually unloads the page.
What I tried:
onpagehide
I found out that onpagehide actually runs console.log("closing") but not recorder.stop() for some reason. Possibly because it runs in the "terminated" state, and no async tasks can run in the terminated state.
onbeforeunload
The popup shows, and the code runs, however the code also runs if the user presses "cancel", and decides not to unload the page.
onunload
The code simply does not run.
I have a console.log() inside my recorder.stop() function, and tried it with the "keep logs" functionality from the inspector.
onvisibilitychange
This one looked promising, but I can't find the right state for it.
document.visibilityState === 'hidden'
Runs anytime I switch to another window, even without closing the previous one.
document.visibilityState === 'terminated'
Most probably won't work, for the same reason as onpagehide()
document.visibilityState === 'frozen'
Doesn't work, probably because the page does not freeze before terminating.

Related

window.onbeforeunload: not assigned unless I set a breakpoint

I am attempting to set a fairly generic OnBeforeUnload handler:
console.log("WCW - Setting browser warning message");
window.onbeforeunload = function(e) {
e.returnValue = "confirmationMessage";
return e.returnValue;
};
console.log("WCW - onbeforeunload is " +
((window.onbeforeunload ? "set":"still null")));
I understand all I can do is signal the browser to issue a fairly generic warning. In a future iteration I may attempt some self-cleanup before issuing the return.
My problem is that window.onbeforeunload always returns null in the console log after this code runs UNLESS I set a breakpoint on any line (e.g., the first log message) and then just 'continue' when it stops. THEN, it works.
I'm quite sure a "what is wrong" question is pointless, so I'm asking, "do you have any advice on how I can look for what might be the problem?".
I wonder if it's a scope issue? The 2nd log message always says onbeforeunload is non-null... But window is a global, isn't it, and how would a debugger pause fix that?
NOTES:
jQuery is available but using $(window).on('beforeunload' is subject to the same issues as this code
adding a delay (setTimeout) to set the handler does not help; tried 2, 4 & 9 seconds
adding a recursive call to add the handler every 4 seconds while the page is open does not result in the handler being set: window.onbeforeunload returns NULL in the console all the time
periodically, I've seen a jQuery core method attached to the event, but it's rare

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();
});

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.

Chrome window onunload event

I'm trying to execute the following code
window.onunload = function () {
var sTag = document.createElement('script');
sTag.src = 'mysrc';
document.getElementsByTagName('head')[0].appendChild(sTag);
return false; };
}
Itseems to work fine in FF but in chrome I'm getting the download status as cancelled as soon as the unload event is fired. I saw some posts in SO with ajax solutions but I'm executing this script inside a cross domain iframe. Im just trying to log the time for which my api was live in a page per visitor. So, I'm sending some time log information on unload of the page. Is there any work around for the above?
For the purpose of what you described, you can have that script loaded in your page or parent window (you are saying it is an iframe right?) and run a function on window.unload:
window.onunload = function(){
window.top.logtime(); // if it is in the parent, or
window.logtime() //if it is in the same window
};
and don't return false, unload event cannot be cancelled. (in best cases, the user gets an alert dialog that will override the return false statement.)
I think what makes this different is how fast it carries out a new function, before the body gets unloaded. Manipulating the DOM is definitely much slower than making a call.

catching beforeunload confirmation canceled?

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.

Categories