Detect If Changes Have Been Made To Input Form - javascript

I have a input form with Jquery & PHP what I am trying to archive is if a user makes any changes to the form and they try to navigate away from the page A popup message comes up saying if they want to exit and discard changes or stay and save the changes first?
How could I archive this thought I did it but all my current one is doing is checking if the fields have data in them but i'm making use of the value='' feature in HTML to get the current settings in the database.
Only run this function if submit has not been pressed yet.

1 Create a flag that specifies whether something was changed:
var changes = false;
2 Bind an event listener to every single input and textarea that sets changes to true:
$('textarea,input').on('keypress change input', function() {
changes = true;
});
3 Bind an event listener to onsubmit that sets changes to false:
$('form').on('submit', function () {
changes = false;
});
4 Bind an event listener to onbeforeunload that returns the question if there are changes:
$(window).on('beforeunload', function () {
if (changes) return "Do you really want to leave?";
});
Demo on JSFiddle: http://jsfiddle.net/TimWolla/VYKeu/

Related

Warn user about unsaved changes to a form, when a form is being replaced during an AJAX call?

I'm new to Javascript and JQuery, and I'm implementing a warning to users that displays when they have made unsaved changes to a form's input/select/textarea elements if they navigate away from a page. I currently have the following which works fine when they leave a static form page:
/*
* Warn users when leaving a page with unsaved content, watches elements: (input, textarea, select) unless
* they are tagged with the "noWarning" class
*/
$(document).ready(function() {
$(document).on('change', 'input:not(.noWarning),textarea:not(.noWarning),select:not(.noWarning)', function () {
window.onbeforeunload = function(e) {
return 'You have unsaved changes';
};
});
});
The only page where it does not work, is in our main editing page. This page, unlike the others, has multiple tabs for editing different aspects of an item, which when clicked, trigger an AJAX call which replaces the content div with the appropriate form for editing the different aspect.
No warning dialog is displayed when a user clicks on a tab though, so any unsaved changes to the input are lost.
My intuition is that because the url is not changing, onBeforeUnload() is not executing. So I would have to check for any changes directly in the function which handles the AJAX call for replacing the form when a tab is clicked:
function clickedTabbedMenu() {
// This function replaces the content div with a new div and form
}
So my question is, how do I go about checking if any changes have been made to the elements in the current form before I replace the content div with another??? Can I directly call the "change" event listener for a true/false??? Or perhaps a different approach to handle this page's warning messages?
Any help is appreciated
Attach a change event handler to all the elements of the form. Have a variable outside the handler's scope dirty (or even a data on the form element) be set to false when a form is loaded, and true on every change event. Then, before replacing the form, check if dirty.
This would also be a good strategy for your non-AJAX pages as well - instead of setting the whole onBeforeUnload each time an element changes, just set onBeforeUnload once, and check if dirty inside it. This makes handling your AJAX and non-AJAX pages very similar.
EDIT: Untested because it's late and I need bed, but basically:
$(document).ready(function() {
var dirty = false;
$(document).on('change', 'input:not(.noWarning),textarea:not(.noWarning),select:not(.noWarning)', function () {
dirty = true;
});
function checkDirty() {
if (dirty) {
alert('You have unsaved changes');
return false;
}
return true;
}
// for non-AJAX pages
window.onbeforeunload = function(e) {
return checkDirty();
};
// for AJAX pages
$('.ajax_navigation_tab').on('click', function() {
if (!checkDirty()) {
// do the ajax thing
dirty = false;
}
});
});
I would try checking for and calling window.onbeforeunload() in your ajax script.
If it exists and returns a string, use it as an error message and halt the operation :)

Jquery onclick seems to prevent checkbox checking

I have a form like the following:
The forms id is 'filter' and I have a jquery onclick function which runs code by using the following line:
$(document).on('click', '#filter', function(){
//on click do the following
This works perfectly. However, I have now added a checkbox to the form. The rest of the form continues to work perfectly but the checkboxes are 'uncheckable'! (if they are set to be pre-checked they stay pre-checked, if they are set to be unchecked they stay unchecked).
The only thing I can think is that the jquery on click function is overriding the checking of the checkboxes. Would this be the case? I can rectify the problem by using blur rather than click but ideally the code would run on click, and in any case id like to understand what is going on! Any ideas?
EDIT
This is the contents of the click handler:
$(document).on('click', '#filter', function(){
//on click do the following
var formData = $(this).serialize(); //put the form names and values into an array called formdata
$.get('filtertest.php',formData,processData); //jquery ajax call
function processData(data){
if(data==1){
$('#content').html('<h2>There is ' + data + ' property available!</h2>');
$('#linky').show();
}
else if(data==0){
$('#content').html('<h2>There are no properties available, please expand your search options.</h2>');
$('#linky').hide();
}
else{
$('#content').html('<h2>There are ' + data + ' properties available!</h2>');
$('#linky').show();
}
}//end processData
return false; //stops the page redirect as per normal operation
});//end submit
Basically the click function looks up matching details from the form in a database and returns the number of matching rows to put in the processdata function
This line is preventing the checkbox from checking:
return false; //stops the page redirect as per normal operation
Using return false will stop event propagation AND prevent the default behavior. The default behavior of a checkbox is to check/uncheck itself when clicked. You might be able to fix this by changing it to:
e.stopPropagation();
This should let the default behavior continue, without propagating any other events that will fire afterwards.
Or you can just take it out if it is no longer needed (based on your comment).

isDirty check not working as desired

I'm using the following script to check if the forms content has changed to prevent the user from browsing away without saving their changes.
<script type="text/javascript">
var isDirty = false;
var msg = 'You have unsaved changes.';
$(document).ready(function(){
$(':input').change(function(){
if(!isDirty){
isDirty = true;
}
});
window.onbeforeunload = function(){
if(isDirty){
return msg;
}
};
});
</script>
The Save button currently looks like this:
<input type="submit" name="frmProjectAction" value="Save" onclick='selectAll(frmProjectResources,true);' />
This is working for the most part but I have two issues:
1. It fires even when the user clicks the Save button on my form. How can I prevent this?
2. It isn't detecting changes in the TinyMCE text box I have on my form. How can I detect changes in the TinyMCE text box as well as all the other fields on my form?
Thanks in advance - Dave
1: use a click() jquery function on the save button that sets isDirty to false. You need to add an id or a class to your save button first so you can target it with jquery. Let's say that you set the save button id to "savebutton" then you do the following:
$('#savebutton').click(function(){
selectAll(frmProjectResources,true); // <-- from your onclick="" attribute
isDirty = false;
})
you can notice that we have moved the selectAll(...) part from the onclick attribute of the save button to the jquery. Hence, you should remove the onclick attribute from the button.
2: TinyMCE has onchange_callback which you can use fire a function which sets isDirty to true. The onchange_callback is set when you initialize TinyMCE, and you give it the name of the function you want to fire whan the form is changed.
//first define the function
function setIsDirty(){
isDirty = true;
}
// then init the tinyMCE
tinyMCE.init({
... // some parameters
onchange_callback : "setIsDirty"
});
This way, you can control precicely what happens when editor is changed. If you just need to check if it has been edited or not before leaving the page, you can also use TinyMCE's own isDirty method
This is really only a solution to question number 1:
You would need one additional event handler, the one for onsubmit on your form.
In that event handler, you update isDirty to false (since you are saving the form now.. so it's no longer "dirty")
I can't comment on tinyMCE, as I've never worked with that.
plugins: "autosave"
autosave_ask_before_unload: true
toolbar: "restoredraft"

jQuery bind to keyup only, not focus

This seems like a simple thing but google hasn't turned up anything for me:
How can I bind to a text / value change event only, excluding an input gaining focus? Ie, given the following:
$(function(){
$('input#target').on('keyup', function(){
alert('Typed something in the input.');
});
});
...the alert would be triggered when the user tabs in and out of an element, whether they actually input text or not. How can you allow a user to keyboard navigate through the form without triggering the event unless they input/change the text in the text field?
Note: I'm showing a simplified version of a script, the reason for not using the change event is that in my real code I have a delay timer so that the event happens after the user stops typing for a second, without them having to change focus to trigger the event.
Store the value, and on any key event check if it's changed, like so:
$(function(){
$('input#target').on('keyup', function(){
if ($(this).data('val')!=this.value) {
alert('Typed something in the input.');
}
$(this).data('val', this.value);
});
});​
FIDDLE
Simply use the .change event.
Update: If you want live change notifications then do you have to go through the keyup event, which means that you need to program your handler to ignore those keys that will not result in the value being modified.
You can implement this with a whitelist of key codes that are ignored, but it could get ugly: pressing Del results in the value being changed, unless the cursor is positioned at the end of the input in which case it does not, unless there happens to be a selected range in the input in which case it does.
Another way which I personally find more sane if not as "pure" is to program your handler to remember the old value of the element and only react if it has changed.
$(function() {
// for each input element we are interested in
$("input").each(function () {
// set a property on the element to remember the old value,
// which is initially unknown
this.oldValue = null;
}).focus(function() {
// this condition is true just once, at the time we
// initialize oldValue to start tracking changes
if (this.oldValue === null) {
this.oldValue = this.value;
}
}).keyup(function() {
// if no change, nothing to do
if (this.oldValue == this.value) {
return;
}
// update the cached old value and do your stuff
this.oldValue = this.value;
alert("value changed on " + this.className);
});
});​
If you do not want to set properties directly on the DOM element (really, there's nothing wrong with it) then you could substitute $(this).data("oldValue") for this.oldValue whenever it appears. This will technically have the drawback of making the code slower, but I don't believe anyone will notice.
See it in action.
This will do it, set a custom attribute and check against that:
$('input').focus(function(){
$(this).attr('originalvalue',$(this).val());
});
$('input').on('keyup',function(){
if($(this).val()===$(this).attr('originalvalue')) return;
alert('he must\'ve typed something.');
});
Be wary of events firing multiple times.
Here is another version that plainly tests if the input field is empty.
If the input is empty then the action is not performed.
$(function(){
$(selector).on('keyup', function(){
if ($(this).val()!='') {
alert('char was entered');
}
})
});

Change event precedence with JQuery

I have the following html code:
<input type="text" id="theInput" value=""/>
Click me
I want to detect when the input changes and perform an operation in this case, but ONLY when the user has not clicked in the link. I have tried this:
$('#theLink').live('click', function(){
alert('click');
});
$('#theInput').live('change', function(){
alert('change');
});
However change is always executed before click when the value in the input changed, due to Javascript event precedence rules, and therefore only "change" message is displayed.
I would like it to display change only if the input value changed and the user exited the input clicking in any other place instead of the link. In that last case I would like to display click.
The example is here.
I use jQuery 1.6.4.
As far as I know, the click event fires after the blur and change events in every browser (have a look at this JSFiddle). The order of blur and change is different across browsers (source: Nicholas Zakas).
To solve your problem, you could listen to click events on the document and compare the event's target with #theLink. Any click event will bubble up to the document (unless it is prevented).
Try this:
var lastValue = '';
$(document).click(function(event) {
var newValue = $('#theInput').val();
if ($(event.target).is('#theLink')) {
// The link was clicked
} else if (newValue !== lastValue) {
// Something else was clicked & input has changed
} else {
// Something else was clicked but input didn't change
}
lastValue = newValue;
});
JSFiddle: http://jsfiddle.net/PPvG/TTwEG/
Both events will fire but in your example the alert in the onchange event handler fired when the onmousedown event occurs will stop the onmouseup event required for the onclick event to fire. Using console.log will show both events firing.
http://jsfiddle.net/hTqNr/4/
Ok, now i got it, you could do
$('#theLink').live('click', function(e){
alert('click');
});
$('#theInput').live('change', function(e){
//Check if the change events is triggerede by the link
if(e.originalEvent.explicitOriginalTarget.data === "Click me"){
//if this is the case trigger the click event of the link
$('#theLink').trigger("click");
}else{
//otherwise do what you would do in the change handler
alert('change');
}
});
Fiddle here http://jsfiddle.net/hTqNr/19/
why you dont pick the value of input box. you have to store initial value of input box on ready function
initialvalue= $('#theInput').val();
then compare the value
$('#theLink').live('click', function(){
var newvalue =$('#theInput').val();
if(newvalue!=initialvalue) {
//do something
}
});

Categories