I have an C#.NET MVC3 web app and I have a question related to the attached Stackoverflow question. I am using a window.beforeunload event to see if there have been changes made on my View. If so, I alert the user that they have unsaved changes. However, if they selected the Create (submit) button, the dialog alerting the user still pops up. I want to NOT pop up the dialog if the Create button is selected. Any ideas? Is there a way to see which control was clicked?
I can think of 2 solutions:
$('#submitBtn').click(function() {
unbindOnBeforeUnload();
});
// OR
// maybe you have multiple cases where you don't want this triggered,
// so this will be better
var shouldTriggerOnBeforeUnload = true;
$('#submitBtn').click(function() {
shouldTriggerOnBeforeUnload = false;
});
...
$(document).unload(function() {
if (shouldTriggerOnBeforeUnload) {
confirm();
}
});
I've written it in a jQuery-like syntax, but only to keep the code concise, you can adapt it to anything you want.
Related
I'm facing a sort of dummy problem.
On my site there is an order form (simple html form) and I noticed that I get double commands from time to time.
I realized that if I clicked repeatedly few times the submit button (before the action page is loaded) I got as many commands as I have clicked.
So I wonder if there are simple solution to make form submission asyncronous?
Thanks
P.S. I added JQuery UI dialog on submit "wait please..." but I get still double commands.
UPDATE
As GeoffAtkins proposed I will:
disable submit after dialog is shown
make use of unique form's token (as it is already added by Symfony) Do not use Symfony token as unique form token as it is always the same for current session. Use just random or something like that.
I would consider doing this (jQuery since you said you used that)
$(function() {
$("#formId").on("submit",function() {
$("#submitBut").hide();
$("#pleaseWait").show();
});
});
if you submit the form and reload the page.
If you Ajax the order, then do
$(function() {
$("#formId").on("submit",function(e) {
e.preventDefault();
var $theForm = $(this);
$("#submitBut").hide();
$("#pleaseWait").show();
$.post($(this).attr("action"),$(this).serialize(),function() {
$theForm.reset();
$("#submitBut").show(); // assuming you want the user to order more stuff
$("#pleaseWait").hide();
});
});
});
NOTE that disabling the submit button on click of the submit button may stop the submission all together (at least in Chrome): https://jsfiddle.net/mplungjan/xc6uc46m/
Just disable the button on click, something like:
$("#my-button-id").on("click", function() {
$(this).attr("disabled", "disabled");
});
var bool = true;
function onclick()
{
if(bool)
{
//do stuff
bool = false;
}
else
{
//ignore
}
}
You could disable the button on the form when it is clicked, and then continue to perform the action. You would probably change the text to say "loading..." or some such.
You may also want to re-enable the button on fail or complete of the ajax request.
I've done this many times similar to this: https://stackoverflow.com/a/19220576/89211
So, I have a hidden file input, and multiple clicks open up multiple file input dialogs. So I disable them temporarily like so:
on click:
$("#file_input").click();
$(document).off(event, #parent_click);
on success, on the change event for the file input
$(document).on(event, #parent_click, clickHandler);
HOWEVER! If the user opens up the File Dialog, and then decides to not do anything, and hits cancel, the #parent_click clickHandler can never be turned on again.
How do I rectify this?
If I correctly got it, you can on .change() check for input value and if it's empty just not to fire disable
$('#yourInputId').change(function{
if($('#yourInputId').val() == ''){
//do some stuff
} else {
//do other stuff
}
})
BTW please, could you provide some fiddle next time, if it's possible, it will be much easier to find out a solution with it.
UPDATED working Fiddle
I want to write Jquery code in master file, so that if there if user changes page and there is any unsaved changes user should get alert.
I got one answer from this: link
However in most solution I will have to write code on all pages. I want it to write only at one place so that everybody dont have to worry to write it in their modules. My code is like:
<script type="text/javascript">
var isChange;
$(document).ready(function () {
$("input[type='text']").change(function () {
isChange = true;
})
});
$(window).unload(function () {
if (isChange) {
alert('Handler for .unload() called.');
}
});
</script>
But everytime i make changes in text boxes .change() event is not firing.
What can be wrong in the code?
EDIT:
I changed .change() to .click and it is fired. i am using jquery 1.4.1..is it because of jquery version that change() is not working?
This is what i am using, Put all this code in a separate JS file and load it in your header file so you will not need to copy this again and again:
var unsaved = false;
$(":input").change(function(){ //triggers change in all input fields including text type
unsaved = true;
});
function unloadPage(){
if(unsaved){
return "You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?";
}
}
window.onbeforeunload = unloadPage;
EDIT for $ not found:
This error can only be caused by one of three things:
Your JavaScript file is not being properly loaded into your page
You have a botched version of jQuery. This could happen because someone edited the core file, or a plugin may have overwritten the $
variable.
You have JavaScript running before the page is fully loaded, and as such, before jQuery is fully loaded.
Make sure all JS code is being placed in this:
$(document).ready(function () {
//place above code here
});
Edit for a Save/Send/Submit Button Exception
$('#save').click(function() {
unsaved = false;
});
Edit to work with dynamic inputs
// Another way to bind the event
$(window).bind('beforeunload', function() {
if(unsaved){
return "You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?";
}
});
// Monitor dynamic inputs
$(document).on('change', ':input', function(){ //triggers change in all input fields including text type
unsaved = true;
});
Add the above code in your alert_unsaved_changes.js file.
A version that use serialization of the form :
Execute this code, when dom ready :
// Store form state at page load
var initial_form_state = $('#myform').serialize();
// Store form state after form submit
$('#myform').submit(function(){
initial_form_state = $('#myform').serialize();
});
// Check form changes before leaving the page and warn user if needed
$(window).bind('beforeunload', function(e) {
var form_state = $('#myform').serialize();
if(initial_form_state != form_state){
var message = "You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?";
e.returnValue = message; // Cross-browser compatibility (src: MDN)
return message;
}
});
If the user change a field then manually rollback, no warn is displayed
change event is fired once the user blurs from input not on every single character inputed.
If you need it to be called every time something is changed (even if focus is still in that input field) you would have to rely on combination of keyup and bunch of events to keep track of pasting/cuting using mouse only.
P.S.
I hope you're aware that your approach to detecting changes isn't the best one? If user input some text, leaves the field and then reverts the changes the script would still alert him about modified text.
you should register events for not only inputs but also textareas, if you mean textarea with text box. You can use keyup for isChange, so that you don't wait for user to blur from this area.
$("input[type='text'], textarea").keyup(function () {
isChange = true;
})
This is really just a different version of #AlphaMale's answer but improved in a few ways:
# Message displayed to user. Depending on browser and if it is a turbolink,
# regular link or user-driven navigation this may or may not display.
msg = "This page is asking you to confirm that you want to leave - data you have entered may not be saved."
# Default state
unsaved = false
# Mark the page as having unsaved content
$(document).on 'change', 'form[method=post]:not([data-remote]) :input', -> unsaved = true
# A new page was loaded via Turbolinks, reset state
$(document).on 'page:change', -> setTimeout (-> unsaved = false), 10
# The user submitted the form (to save) so no need to ask them.
$(document).on 'submit', 'form[method=post]', ->
unsaved = false
return
# Confirm with user if they try to go elsewhere
$(window).bind 'beforeunload', -> return msg if unsaved
# If page about to change via Turbolinks also confirm with user
$(document).on 'page:before-change', (event) ->
event.preventDefault() if unsaved && !confirm msg
This is better in the following ways:
It is coffeescript which IMHO automatically makes it better. :)
It is entirely based on event bubbling so dynamic content is automatically handled (#AlphaMale's update also has this).
It only operates on POST forms as GET forms do not have data we typically want to avoid loosing (i.e. GET forms tend to be search boxes and filtering criteria).
It doesn't need to be bound to a specific button for carrying out the save. Anytime the form is submitted we assume that submission is saving.
It is Turbolinks compatible. If you don't need that just drop the two page: event bindings.
It is designed so that you can just include it with the rest of your JS and your entire site will be protected.
Why not simply bind the event to the change callback?
$(":input").change(function()
{
$(window).unbind('unload').bind('unload',function()
{
alert('unsaved changes on the page');
});
});
As an added bonus, you can use confirm and select the last element that triggered the change event:
$(":input").change(function()
{
$(window).unbind('unload').bind('unload',(function(elem)
{//elem holds reference to changed element
return function(e)
{//get the event object:
e = e || window.event;
if (confirm('unsaved changes on the page\nDo you wish to save them first?'))
{
elem.focus();//select element
return false;//in jQuery this stops the event from completeing
}
}
}($(this)));//passed elem here, I passed it as a jQ object, so elem.focus() works
//pass it as <this>, then you'll have to do $(elem).focus(); or write pure JS
});
If you have some save button, make sure that that unbinds the unload event, though:
$('#save').click(function()
{
$(window).unbind('unload');
//rest of your code here
});
Without jQuery:
var unsaved = false;
document.addEventListener("DOMContentLoaded", function() {
var els = document.querySelectorAll('textarea, input, select');
els.forEach( function(el) {
el.addEventListener('change', function() {
unsaved = true;
});
});
window.addEventListener('beforeunload', function(event) {
if(unsaved){
event.returnValue = "string";
}
});
var forms = document.querySelectorAll('form');
forms.forEach( function(form) {
form.addEventListener('submit', function() {
unsaved = false;
});
});
});
The weird 'string' hack explanation can be found here.
I use $('form').change etc. function to set a dirty bit variable. Not suitable to catch all changes (as per previous answers), but catches all that I'm interested in, in my app.
I am working with a .Net 1.1 web application. There is a Save button that, when clicked, will pop up the Javascript confirm box. Once the user clicks OK a long running process is kicked off. We would like to show a busy indicator when the user clicks the OK button of the confirm dialog. Can this be done?
if(confirm("Are you sure you would like to save?")){
alert("Loading") //Replace with what you want to do
}
The button on an alert() dialog is not scriptable. You need to find where in the code the alert() is called and patch in to the code just after this.
What you want to do is pretty simple. A confirm('Text') returns true or false after the user makes their selection. All you need to do is on true show a busy indication.
Here is what you're looking for http://jsfiddle.net/Akkuma/C6ZZf/
$('#save').on('click', function () {
var shouldSave = confirm('Make your choice');
if(shouldSave) {
alert('Do saving');
}
else {
alert('Not saving');
}
});
Is there a way to capture to result of the window.onbeforeunload confirmation dialog like the one below from Stack Overflow (this happens when leaving the 'Ask Question' page without posting the question)?
This is how it appears in Chrome, I believe it's slightly different in other browsers, but you always have some form of yes/no buttons.
Presumably if they're still on the offending page after the event has been triggered they chose to stay and you could probably figure this out by watching the sequence of js. However I would like to know how to determine if they clicked "Leave this page"?
I've implemented this like below:
// concept taken from SO implementation
function setConfirmUnload(showMessage, message) {
window.onbeforeunload = showMessage ? (message ? message : "this is a default message") : null;
}
// pseudo code
listen to changes on inputs
if any inputs fire a change event
call setConfirmUnload(true, 'My warning message')
note I'm using jQuery within my site.
I'm essentially trying to implement a Gmail like drafting implementation, wherein if a user leaves a page with a form they've made changes to without saving they're warmed with a similar dialog. If they choose to discard they're changes and leave the page, I need to clean up some temporary records from the database (I'm thinking an AJAX call, or simply submitting the form with a delete flag) then sending them on their way.
My question also relates to:
jQuery AJAX call in onunload handler firing AFTER getting the page on a manual refresh. How do I guarantee onunload happens first?
You can have the exit confirmation using window.onbeforeunload but there isn't a way to find out which button the user clicked on.
To quote an earlier response from jvenema from this thread:
The primary purpose for the
beforeunload is for things like
allowing the users the option to save
changes before their changes are lost.
Besides, if your users are leaving,
it's already too late [...]
How about this:
$( window ).bind( 'beforeunload' , function( event ) {
setTimeout( function() {
alert( 'Hi againe!' );
} );
return '';
} ).bind( 'unload', function( event ) {
alert( 'Goodby!' );
} );
Late to the party, but I found the following code (in TypeScript) to be a decent way to detect if the person clicked on 'Ok' on that confirmation dialogue window.
public listenToUnloadEvents(): void {
window.addEventListener('beforeunload', (e) => {
const confirmationMessage = '\o/';
(e || window.event).returnValue = confirmationMessage; // Gecko + IE
return confirmationMessage; // Webkit, Safari, Chrome etc.
});
window.addEventListener('unload', () => {
this.sendNotification(Action.LEFT)
});
}
I'm not sure how much time you have to run code in the unload event, but in this instance, I am sending a notification through Socket.io, so it's very quick at completing.
As for detecting the cancel on that notification, as someone else mentioned, creating a global variable like let didEnterBeforeUnload = false could be set to true when the beforeunload event fires. After this, by creating the third event, like so (again, in TypeScript), you can infer the user pressing cancel
window.addEventListener('focus', (e) => {
if (didEnterBeforeUnload) {
console.log('pressed cancel')
}
didEnterBeforeUnload = false
});
As a side-note though, these events won't (iirc) fire unless you have interacted with the page. So make sure to click or tap into the page before trying to navigate away during your testing.
I hope this helps anyone else out there!