Detecting form changes on beforeunload? - javascript

This has already been asked, I know, but I don't understand why this isn't working for me.
http://jsfiddle.net/M9tnP/16/
Trying to detect form changes, and if the user tries to navigate away, I want them to confirm that they will lose their changes...
var dirty = false;
$("form#add_item :input").change(function() {
dirty = true;
});
$("window").on('beforeunload', function() {
if (dirty) {
return 'You have unsaved changes! If you leave this page, your changes will be lost.';
}
});​

You need to set the event handler for onbeforeunload directly on the window object:
var dirty = false;
$("form#add_item :input").change(function() {
//alert("form changed!")
dirty = true;
});
window.onbeforeunload = function() {
if (dirty) {
return 'You have unsaved changes! If you leave this page, your changes will be lost.';
}
};​
Working example: http://jsfiddle.net/M9tnP/20/

You need:
$(window).on('beforeunload', function() {...})
, not:
$("window").on('beforeunload', function() {...})
(no quotes around window). $(window) wraps the window javascript variable inside a jQuery object, where $("window") selects all <window> tags on the page, of which there are exactly 0.
(Most of the time. In fact you can do $('body').append('<window></window>'), and then $("window") matches that new tag - but don't do that.)

Related

Page is not jumping if window.onhashchange is used

I am using hashchange event to ask a confirmation dialog box saying that the data in the current page will be lost, if moved.
I've implemented like this:
window.onhashchange = function() {
if(window.confirm("Do you really want to close?")){
}
};
This event is triggered correctly. But the page is not changing if the user clicks yes. What do I need in the {...}?
Please try
window.onhashchange = function() {
if (window.confirm("Do you really want to navigate?")) {
location.hash = location.hash; // or set the hash elsewhere and change here
}
};

How to prevent navigation by browser buttons but not by links?

I want warn users if they leave the page by closing the browser or using the history buttons of the browser using the following javascript:
window.onbeforeunload = function(e) {
return 'Ask user a page leaving question here';
};
But my links and buttons on my website should work regardless of this. How can I achieve that?
The first way that comes to mind is to set a variable that tells you whether a link was clicked:
var linked = false;
window.onbeforeunload = function(e) {
if (!linked)
return 'Ask user a page leaving question here';
};
document.addEventListener('click', function(e) {
if (e.target.tagName === "A")
linked = true;
}, false);
That is, set a click event handler at the document level, that tests whether the clicked element was an anchor (or whatever else you want to allow) and if so sets the variable. (Obviously this assumes that you don't have other anchor element click handlers at a lower level that stop event propagation.)
var linkClicked = false;
window.onbeforeunload = function(e) {
if (!linkClicked){
linkClicked = false;
return 'Ask user a page leaving question here';
}
};
$(document).ready(function(){
$('a').click(function(e){
linkClicked = true;
});
});
Obviously this relies on JQuery to add the event handler to all links, but you could attach the handler with any other method, including adding onclick="linkClicked=true;" to every link on the page if you really have to.
Edit:
Just want to point out that if the user clicks a link that doesn't redirect them (e.g. a hashtag link to somewhere else on the page, or something that returns false / prevents the default action being executed) then this will set linkClicked to true and subsequently any browser based navigation won't be caught.
If you want to catch this, I would advise setting a timeout on the link click like so:
$(document).ready(function(){
$('a').click(function(e){
linkClicked = true;
setTimeout(function(){
linkClicked = false;
}, 500);
});
});
This will allow half a second for the window unload event to trigger before resetting the flag so that future navigation events are caught correctly. This still isn't perfect, but it probably doesn't need to be.
You can use the window.onbeforeunload event.
var check= false;
window.onbeforeunload = function () {
if (!check) {
return "Are you sure you want to leave this page?"
}
}
function CheckBackButton() {
check= true;
}
referenceElement.addEventListener('onClick', CheckBackButton(), false);
Us a confirmation prompt no?
like this? Intercept page exit event
window.onbeforeunload = function (e) {
var message = "Your confirmation message goes here.",
e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = message;
}
// For Safari
return message;
};
How to show the β€œAre you sure you want to navigate away from this page?” when changes committed? this may solve your problem How

Detecting form changes using jQuery when the form changes themselves were triggered by JS

I have a list of radio buttons that I can toggle "yes" or "no" to using Javascript.
$(document).ready(function(){
$('#select-all').click(function(){
$('#notifications .notif-radio').each(function(){
$('input[type="radio"]', this).eq(0).attr('checked', true);
$('input[type="radio"]', this).eq(1).attr('checked', false);
});
});
$('#deselect-all').click(function(){
$('#notifications .notif-radio').each(function(){
$('input[type="radio"]', this).eq(0).attr('checked', false);
$('input[type="radio"]', this).eq(1).attr('checked', true);
});
});
});
this works just fine. Now I have a separate piece of code that detects when a user has changed something, and asks them if they want to leave the page.
var stay_on_page;
window.onbeforeunload = confirm_exit;
$('.container form input[TYPE="SUBMIT"]').click(function(){
stay_on_page = false;
});
$('#wrapper #content .container.edit-user form').change(function(){
stay_on_page = true;
});
function confirm_exit()
{
if(stay_on_page){ return "Are you sure you want to navigate away without saving changes?"; }
}
The problem is that if the user uses the first piece of functionality to toggle all radio buttons one way or another. The JS detecting form changes doesn't see that the form was changed. I have tried using .live, but to no avail. Anyone have any ideas?
I do something similar to this by adding change() (or whatever's appropriate, click() in your case I suppose) event handlers which set either a visible or hidden field value, then check that value as part of your onbeforeunload function.
So, my on before unload looks like:
window.onbeforeunload = function () {
if ($('#dirtymark').length) {
return "You have unsaved changes.";
}
};
And, or course, dirtymark is added to the page (a red asterisk near the Save button), when the page becomes dirty.

Looking for a better workaround to Chrome select on focus bug

I have the same problem as the user in this question, which is due to this bug in Webkit. However, the workaround provided will not work for my app. Let me re-state the problem so that you don't have to go read another question:
I am trying to select all the text in a textarea when it gets focus. The following jQuery code works in IE/FF/Opera:
$('#out').focus(function(){
$('#out').select();
});
However, in Chrome/Safari the text is selected--very briefly--but then the mouseUp event is fired and the text is deselected. The following workaround is offered in the above links:
$('#out').mouseup(function(e){
e.preventDefault();
});
However, this workaround is no good for me. I want to select all text only when the user gives the textarea focus. He must then be able to select only part of the text if he chooses. Can anyone think of a workaround that still meets this requirement?
How about this?
$('#out').focus(function () {
$('#out').select().mouseup(function (e) {
e.preventDefault();
$(this).unbind("mouseup");
});
});
The accepted answer (and basically every other solution I found so far) does not work with keyboard focus, i. e. pressing tab, at least not in my Chromium 21. I use the following snippet instead:
$('#out').focus(function () {
$(this).select().one('mouseup', function (e) {
$(this).off('keyup');
e.preventDefault();
}).one('keyup', function () {
$(this).select().off('mouseup');
});
});
e.preventDefault() in the keyup or focus handler does not help, so the unselecting after a keyboard focus seems to not happen in their default handlers, but rather somewhere between the focus and keyup events.
As suggested by #BarelyFitz, it might be better to work with namespaced events in order to not accidentally unbind other event handlers. Replace 'keyup' with 'keyup.selectText' and 'mouseup' with 'mouseup.selectText' for that.
Why not simply:
$('#out').focus(function(){
$(this).one('mouseup', function() {
$(this).select();
});
});
Seems to work in all major browsers...
A very slightly different approach would be to separate the focus event from the mouse sequence. This works really nicely for me - no state variables, no leaked handlers, no inadvertent removal of handlers, and it works with click, tab, or programmatic focus. Code and jsFiddle below -
$('#out').focus(function() {
$(this).select();
});
$('#out').on('mousedown.selectOnFocus', function() {
if (!($(this).is(':focus'))) {
$(this).focus();
$(this).one('mouseup.selectOnFocus', function(up) {
up.preventDefault();
});
}
});
https://jsfiddle.net/tpankake/eob9eb26/27/
Make a bool. Set it to true after a focus event and reset it after a mouse up event. During the mouse up, if it's true, you know the user just selected the text field; therefore you know you must prevent the mouse up from happening. Otherwise, you must let it pass.
var textFieldGotFocus = false;
$('#out').focus(function()
{
$('#out').select();
textFieldGotFocus = true;
});
$('#out').mouseup(function(e)
{
if (textFieldGotFocus)
e.preventDefault();
});
$(document).mouseup(function() { textFieldGotFocus = false; });
It's important that you put the mouseup listener that resets the variable on document, since it's not guaranteed that the user will release the mouse button over the text field.
onclick="var self = this;setTimeout(function() {self.select();}, 0);"
Select the text before putting the focus on the input box.
$('#out').select().focus();
digitalfresh's solution is mostly there, but has a bug in that if you manually trigger .focus() using JS (so not using a click), or if you tab to the field, then you get an unwanted mouseup event bound - this causes the first click that should deselect the text to be ignored.
To solve:
var out = $('#out');
var mouseCurrentlyDown = false;
out.focus(function () {
out.select();
if (mouseCurrentlyDown) {
out.one('mouseup', function (e) {
e.preventDefault();
});
}
}).mousedown(function() {
mouseCurrentlyDown = true;
});
$('body').mouseup(function() {
mouseCurrentlyDown = false;
});
Note: The mouseup event should be on body and not the input as we want to account for the user mousedown-ing within the input, moving the mouse out of the input, and then mouseup-ing.
tpankake's answer converted to a reusable jQuery function..
(If you upvote this, please also upvote his answer)
Load the following AFTER loading the jQuery library:
$.fn.focusSelect = function () {
return this.each(function () {
var me = $(this);
me.focus(function () {
$(this).select();
});
me.on('mousedown.selectOnFocus', function () {
var me2 = $(this);
if (me2.is(':focus') === false) {
me2.focus();
me2.one('mouseup.selectOnFocus', function (up) {
up.preventDefault();
});
}
});
});
};
Use it like this:
$(document).ready(function () {
// apply to all inputs on the page:
$('input[type=text]').focusSelect();
// apply only to one input
$('#out').focusSelect();
});

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

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.

Categories