window.beforeunload called twice in Firefox - how to get around this? - javascript

I'm creating a popup window that has a beforeunload handler installed. When the "Close" file menu item is used to close the popup, the beforeunload handler is called twice, resulting in two "Are you sure you want to close this window?" messages appearing.
This is a bug with Firefox, and I've reported it here, but I still would like a way to prevent this from happening. Can you think of a sane way of detecting double beforeunload to prevent the double message problem? The problem is that Firefox doesn't tell me which button in the dialog the user elected to click - OK or cancel.

<script type="text/javascript">
var onBeforeUnloadFired = false;
window.onbeforeunload = function ()
{
if (!onBeforeUnloadFired) {
onBeforeUnloadFired = true;
event.returnValue = "You have attempted to leave this page. If you have made any changes to the fields without clicking the Save button, your changes will be lost. Are you sure you want to exit this page?";
}
window.setTimeout("ResetOnBeforeUnloadFired()", 10);
}
function ResetOnBeforeUnloadFired() {
onBeforeUnloadFired = false;
}
</script>

Set a variable in the handler to prevent the dialog coming up the second time. Use setTimeout to reset it afterwards.

This is definitely a FF bug. I've reported it at https://bugzilla.mozilla.org/show_bug.cgi?id=531199

The best solution I've found is to use a flag global variable that is reset after so many milliseconds, say 500 (this ensures that the function can be called again, but not immediately after its appearance).
See last code in:
http://social.msdn.microsoft.com/Forums/en/sharepointinfopath/thread/13000cd8-5c50-4260-a0d2-bc404764966d

I've found this problem in Chrome 21, Firefox 14, IE 7-9, Safari 5 (on PC).
The following works on all of these browsers. If one removes the window.onbeforeunload function during the event this will prevent the second call. The trick is to reset the window.onbeforeunload function if the user decides to stay on the page.
var window_on_before_unload = function(e) {
var msg;
// Do here what you ever you need to do
msg = "Message for user";
// Prevent next "window.onbeforeunload" from re-running this code.
// Ensure that if the user decides to stay on the page that
// this code is run the next time the user tries to leave the page.
window.onbeforeunload = set_on_before_unload;
// Prepare message for user
if (msg) {
if (/irefox\/([4-9]|1\d+)/.test(navigator.userAgent))
alert(msg
+ '\n\nThe next dialog will allow you to stay here or continue\nSee Firefox bug #588292');
(e = e || window.event).returnValue = msg;
return msg;
}
};
// Set window.onbeforeunload to the above handler.
// #uses window_on_before_unload
// #param {Event} e
var set_on_before_unload = function(e) {
// Initialize the handler for window.onbeforeunload.
window.onbeforeunload = window_on_before_unload;
}
// Initialize the handler for window.onbeforeunload.
set_on_before_unload();

Create a global variable that is set to true inside the handler. Only show the alert/popup when this variable is false.

I use the following snippet to track the exitcount
When the page loads the following variable exitCount is initialized
if (typeof(MTG) == 'undefined') MTG = {};
MTG.exitCount=0;
and in the Window unload event
$(window).bind("beforeunload", function(){
if (MTG.exitCount<=0)
{
//do your thing, save etc
}
MTG.exitCount++;
});

I've found that instead of doing your own call to confirm(), just do even.preventDefault(); within the beforeunload event. Firefox throws up its own confirm dialog.
I'm not sure if this is the correct/standard thing to do, but that's how they're doing it.

I have a document opening another popup window with window.open. In the original window I have registered (with jquery) a listener for "unload" event like this:
var popup_window = window.open(...)
$(popup_window).on('unload', function(event) ...
I have came across this page because the event was effectively triggering twice. What I have found is that it is not a bug, it triggers twice because it fires once for "about:blank" page being replaced by your page and another for your page being unloaded.
All I have to do is to filter the event that I am interested in by querying the original event:
function (event) {
var original_url = e.originalEvent.originalTarget.URL;
if (original_url != 'about:blank')
{
... do cool things ...
}
}
I don't know if this applies to the original question, because it is a special case of a window opening another, but I hope it helps.

Related

How to lose trusted context from click event

I'm trying to create a userscript that causes the native popup blocker to apply to all popups, even those that were the result of user interaction.
I came up with the following idea:
window.addEventListener('click', function(e) {
console.log(e.isTrusted);
if (e.isTrusted) {
e.stopImmediatePropagation();
e.preventDefault();
e.target.dispatchEvent(new e.constructor(e.type, e));
}
}, true);
button.addEventListener('click', function(e) {
window.open('about:blank');
});
<button id="button">Test</button>
(In the snippet window.open won't work because of the iframe sandbox.)
Basically the idea is to add an event listener to the page that replaces any click event that is trusted with a copy of it that isn't trusted. However this doesn't work and the popup is still opened.
Is there any way to accomplish this?
Relevant specification here:
https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation
This just answers the X part of this XY problem, because I see no real use for the Y.
If you wish to block all popups, then window.open = null; will already block all the ones made from this method, then you may also want to block the ones from anchor elements,
document.addEventListener('click', e => {
if(e.target.nodeName === 'A' && e.target.target === "_blank") {
e.preventDefault();
}
});
Now, you'll have to apply this in all the documents (i.e in iframes too) and you should be good.
But note that there are many legit reasons for pages to open popup windows, and disabling it will definitely break a lot of websites.
The pop-blocker's handling of event contexts seems to be complex. Even generating a setTimeout with an evaluated string does not break the context. In Firefox there is a period of one second after a click to perform a popup, later it is cosidered not to be triggered by a click.
However, I could get Firefox to block popups using setInterval. I did not test it yet in Chrome.
We overwrite the window.open method with a custom one:
window.open = (function()
{
const
openArgs = [],
fnOpen = window.open.bind(window)
;
setInterval( () => { for(let args; args = openArgs.pop(); fnOpen(args)); }, 100);
return function open(...args) { openArgs.push(args); }
})();
<button onclick="window.open('http://example.com');">button</button>
open

My requirement is that I want to show a message to user while closing the browser tab or window

I am trying using following code-
window.onunload = function(e){
return "Do you really want to quit without saving."
}
**
But message is also appearing if I will try to navigate from one page
to another
**. I only want this functionality if user clicks on the [x] button not on any event change.
I have also tried following-
window.onbeforeunload = function(e){
return "Somethig"
}
Note- I want to identify the event when user only closing the browser, not for any other page event.
Using Jquery:
$(window).unload(function() {
//your code
});
using javasscript:
window.onbeforeunload = function(e){
var displaymessage = 'Are you sure?';
e = e || window.event;
if(e)
e.returnValue = displaymessage;
return displaymessage;
}
This question is a duplicate of this question.
You can't modify the default dialogue for onbeforeunload, so your best bet may be to work with it.
Also, in recent versions of Chrome, the feature has been deprecated.
Edit 09/04/2018: custom messages in onbeforeunload dialogs are deprecated since chrome-51 (cf: release note)

onbeforeunload keeps poping up an alert box when user click something

I am created a page that warns the user when they click on the (close x) button on the window. I did some reading and discovered that JavaScript had a function called onbeforeonload which can take of the job I was trying to achieve. I however found at after my implementation that, when a user clicks on anything in my window (example: save and enter) The dialog box reappears. I was wondering how I could only target the specific X button in the window.
window.onbeforeunload = function (evt) {
var message = 'Do you want to leave?';
if (typeof evt == 'undefined') {
evt = window.event;
}
if (evt) {
evt.returnValue = message;
}
return message;
}
Right now the function is being called globally... this resource might help you achieve what you are looking for: http://randomdrake.com/2009/09/23/how-to-use-onbeforeunload-with-form-submit-buttons/
This is a "working as intended" behavior for IE. Anchor tag clicks, regardless of whether they navigate or not, will trigger the onbeforeunload event.
This is the workaround I used - I am not sure whether it is the best approach or not:
document.onmouseup = function () {
if (window.event.srcElement.tagName === 'A') {
// turn off your onbeforeunload handler
...
// some small time later, turn it back on
setTimeout(..., 200);
}
};

window bind POPSTATE

Given the following:
$(window).bind("popstate", function() {
alert('popstate');
});
On first load, the alert fires with FireFox and Chrome but not Safari. Why is that? Anyone else seen this and know how to best solve for this?
See the code from pjax. pjax is fairly popular open source library now, so the below logic might be the best to avoid this issue.
var popped = ('state' in window.history), initialURL = location.href
$(window).bind('popstate', function(event) {
// Ignore inital popstate that some browsers fire on page load
var initialPop = !popped && location.href == initialURL
popped = true
if ( initialPop ) return
...
https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js
In webkit the popstate event is fired on page load. To avoid this, this easy work around works for me:
Every time I fire history.push(...) I add the class historypushed to the body-tag:
history.push(...);
$("body").addClass("historypushed");
When I trigger the popstate event, I check for this class:
$(window).bind('popstate', function(e) {
if($("body").hasClass("historypushed")) {
/* my code */
}
});
The situation is now reversed. Chrome has fixed the bug and now fires popstate on page load but Firefox 4 (since RC) has departed from the spec and now does not fire popstate!
UPDATE: The HTML5 spec was changed in 2011 to state popstate should not fired on page load. Both Firefox and Chrome now do the right thing as of Firefox 4 and Chrome 34.
An easy way to avoid this issue is to set the first argument on pushState to true then check against onpopstate. Similar to the pjax example but a bit more straightforward. The below example will run doSomething() for every popstate event except for the first page load.
function setupPopState() {
if (history.popState) {
# immediately replace state to ensure popstate works for inital page
history.replaceState(true, null, window.location.pathname);
$(window).bind('popstate', function(event) {
if (event.originalEvent.state) {
doSomething();
}
});
}
}
There was a bug in Webkit that incorrectly implemented the "popstate" event. Check out this simple post explaining the problem (cool little show and tell): http://www.bcherry.net/playground/pushstate
My suggestion would be to implement your own "popstate" event tracker for Safari. Something like this:
$(window).load(function(){
function fire_popstate(){
$(this).trigger("popstate"); // fire it when the page first loads
}
var lasthash = window.location.hash;
setInterval(function(){
var currenthash = window.location.hash;
if(lasthash != currenthash){
fire_popstate();
}
}, 500);//check every half second if the url has changed
});
You could wrap that statement in a browser test to check for safari. Even better see if "popstate" has been fired by the time the DOM is ready and then apply the inner function to replace the implementation. The one thing you don't want to happen is have two popstate events to be fired (duplicating your event handler logic, great way to lock up the UI).
This is my workaround.
window.setTimeout(function() {
window.addEventListener('popstate', function() {
// ...
});
}, 1000);
I convert #Nobu & #Tamlyn answers into an object, I also add a little
fix by adding "window.history.state !== null". In some browsers the
history.state exists, but it's null so the it was not working.
/**
* The HTML5 spec was changed in 2011 to state popstate should not
* fired on page load. Chrome(34) & Firefox(4) has fixed the bug but
* some browsers (e.g. Safari 5.1.7) are still fire the popstate on
* the page load. This object created from the Pjax Library to handle
* this issue.
*/
var popstatePageloadFix = {
popped : ('state' in window.history && window.history.state !== null),
initialUrl : location.href,
initialPop : false,
init : function() {
this.initialPop = !this.popped && location.href == this.initialUrl;
this.popped = true;
return this.initialPop;
}
};
$(window).on("popstate", function (event) {
// Ignore initial popstate that some browsers fire on page load
if ( popstatePageloadFix.init() ) return;
...
});
Thanks #Nobu!
Thanks #Tamlyn!
This answer to a similar question suggests to check for boolean truth of event.state in the popstate event handler:
window.addEventListener('popstate', function(event) {
if (event.state) {
alert('!');
}
}, false);
You can also tie your callback function to popstate event like this:
window.onpopstate = callback();
Check here for more information on that solution

javascript to check when the browser window is closed

Does anyone know any way that I can use javascript to check when the browser window is closed and pop-up a confirmation dialog to ask whether the user is confirm to exit the browser or change his mind to stay?
window.onbeforeunload = function (e) {
var e = e || window.event;
//IE & Firefox
if (e) {
e.returnValue = 'Are you sure?';
}
// For Safari
return 'Are you sure?';
};
https://developer.mozilla.org/en/DOM/window.onbeforeunload
The documentation here encourages listening to the onbeforeunload event and/or adding an event listener on window.
window.addEventListener('beforeunload', function(e) {}, false);
You can also just populate the .onunload or .onbeforeunload properties of window with a function or a function reference.
Though behaviour is not standardized across browsers, the function may return a value that the browser will display when confirming whether to leave the page.
If the browser remains running after the page is closed, and if the browser processes the "onbeforeunload" event of the body element (sometimes it's disabled), and if the browser allows popup windows or mesage boxes and the ability to return false from that event to prevent the page change, then it's possible.
For an example, start typing a comment on any stackoverflow page with Javascript enabled and then navigate away from that page.
This worked for me:
function closeWin(){
var exit = confirm("Do you want to leave this window?");
if(exit==true){
//do something before closing;
}
}
body onbeforeunload="closeWin()"
This works too, unless for IE8
$(window).bind('beforeunload', function (e) {
// code to execute when browser is closed
e.$.post("func.php", { action: 'action', id_userMsg: '<?php echo $id_user; ?>' });
});

Categories