91

In jQuery, is there a simple way to test if any of a form's elements have changed?

Say I have a form and I have a button with the following click() event:

$('#mybutton').click(function() {
  // Here is where is need to test
  if(/* FORM has changed */) {
     // Do something
  }
});

How would I test if the form has changed since it was loaded?

F1Krazy
  • 146
  • 1
  • 4
  • 14
mike
  • 8,041
  • 19
  • 53
  • 68
  • 1
    Do you mean by action of some other script included in the page or as soon as the user has typed some text or clicked a radio/checkbox? – FelipeAls Jun 11 '10 at 18:51
  • I should have added that I only need to check on a click() event – mike Jun 11 '10 at 18:55

13 Answers13

151

You can do this:

$("form :input").change(function() {
  $(this).closest('form').data('changed', true);
});
$('#mybutton').click(function() {
  if($(this).closest('form').data('changed')) {
     //do something
  }
});

This rigs a change event handler to inputs in the form, if any of them change it uses .data() to set a changed value to true, then we just check for that value on the click, this assumes that #mybutton is inside the form (if not just replace $(this).closest('form') with $('#myForm')), but you could make it even more generic, like this:

$('.checkChangedbutton').click(function() {
  if($(this).closest('form').data('changed')) {
     //do something
  }
});

References: Updated

According to jQuery this is a filter to select all form controls.

http://api.jquery.com/input-selector/

The :input selector basically selects all form controls.

Val
  • 17,336
  • 23
  • 95
  • 144
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • 7
    Does `$('form :input')` account for the select, textarea and other input types ? or is it specific to the input tag? – Val May 31 '13 at 09:42
  • 6
    Correct me if I'm wrong, but it seems like, this method will say the form has been changed, even if someone makes a change and then "undoes" their change, manually reverting back to the original values in the form. In this case the form really wouldn't have changed with respect to the data, yet this method would say it had. – paul Mar 10 '14 at 22:46
  • @paul that's correct, you could serialize the form when the page starts and compare to a current serialization if you wanted to see if any net change - that's just more complicated than for most peoples' needs – Nick Craver Mar 11 '14 at 01:40
  • 1
    Thanks, yea I tried that (it's listed in another answer) but it didn't work for me. It seems like there are some special cases that trip that method up as well. Currently working with a plugin - https://github.com/codedance/jquery.AreYouSure – paul Mar 11 '14 at 16:29
  • 1
    Good solution for this case! But if someone here wants to get the state of the form's content (like "IsDirty"), you might want to follow this solution: http://stackoverflow.com/a/26796840/665783 – Jacob May 05 '17 at 01:55
  • 1
    @Nick Craver : What if the form element is outside the form tag with form attribute as the id form the form. I have tested the case, bad luck it is not working. – Mahantesh Apr 20 '19 at 10:26
54

If you want to check if the form data, as it is going to be sent to the server, have changed, you can serialize the form data on page load and compare it to the current form data:

$(function() {

    var form_original_data = $("#myform").serialize(); 

    $("#mybutton").click(function() {
        if ($("#myform").serialize() != form_original_data) {
            // Something changed
        }
    });

});
Udi
  • 29,222
  • 9
  • 96
  • 129
  • 5
    Excellent, this is the best solution out here. Thanks. – Anonymous Sep 07 '12 at 19:05
  • 3
    I used this method once, and it became very slow when the form became bigger. This is a good method for small forms, but it is not the way to go for large forms. – mkdevoogd Apr 22 '13 at 08:13
  • do you mean forms with files in them? – Udi Apr 22 '13 at 17:26
  • thanks,it's very helpfull fore me. simple and effective solution. – Evgeny Zagorulko Feb 18 '14 at 11:48
  • 1
    This is good, but it would be better if instead of checking each time a control changes, directly check when the form is about to be submitted – Ruby Racer Aug 13 '14 at 06:19
  • @RubyRacer: Modern JS is very fast. You can enable/disable the submit button for example on control change. – Udi Aug 14 '14 at 07:11
  • 2
    This is much better, so you can trigger buttons back to disabled, like "delete" – EliuX Sep 24 '15 at 17:27
  • One draw back for me of this method, is fields that were intended for numbers only match if they include the same string ("0" "0.0", "0.000") are not considered to be the same. In my case I had to write something more specific that took account of casting floats etc. – dading84 Dec 18 '16 at 12:44
  • another drawback to this is if you are doing form validation (which I hope you are) any time the form submit fails validation it would cause any entries which had been changed from original (in the case of say, editing an object from a database where the form gets pre-populated with the data) to show up as 'unchanged' according to this method. – codeAndStuff Jul 10 '19 at 19:15
41

A real time and simple solution:

$('form').on('keyup change paste', 'input, select, textarea', function(){
    console.log('Form changed!');
});
Marcio Mazzucato
  • 8,841
  • 9
  • 64
  • 79
10

You can use multiple selectors to attach a callback to the change event for any form element.

$("input, select").change(function(){
    // Something changed
});

EDIT

Since you mentioned you only need this for a click, you can simply modify my original code to this:

$("input, select").click(function(){
    // A form element was clicked
});

EDIT #2

Ok, you can set a global that is set once something has been changed like this:

var FORM_HAS_CHANGED = false;

$('#mybutton').click(function() {
    if (FORM_HAS_CHANGED) {
        // The form has changed
    }
});

$("input, select").change(function(){
    FORM_HAS_CHANGED = true;
});
Joe D
  • 3,588
  • 2
  • 19
  • 14
3

Looking at the updated question try something like

$('input, textarea, select').each(function(){
    $(this).data("val", $(this).val());
});
$('#button').click(function() {
    $('input, textarea, select').each(function(){
        if($(this).data("val")!==$(this).val()) alert("Things Changed");
    });
});

For the original question use something like

$('input').change(function() {
    alert("Things have changed!");
});
Pez Cuckow
  • 14,048
  • 16
  • 80
  • 130
2

Here is an elegant solution.

There is hidden property for each input element on the form that you can use to determine whether or not the value was changed. Each type of input has it's own property name. For example

  • for text/textarea it's defaultValue
  • for select it's defaultSelect
  • for checkbox/radio it's defaultChecked

Here is the example.

function bindFormChange($form) {

  function touchButtons() {
    var
      changed_objects = [],
      $observable_buttons = $form.find('input[type="submit"], button[type="submit"], button[data-object="reset-form"]');

    changed_objects = $('input:text, input:checkbox, input:radio, textarea, select', $form).map(function () {
      var
        $input = $(this),
        changed = false;

      if ($input.is('input:text') || $input.is('textarea') ) {
        changed = (($input).prop('defaultValue') != $input.val());
      }
      if (!changed && $input.is('select') ) {
        changed = !$('option:selected', $input).prop('defaultSelected');
      }
      if (!changed && $input.is('input:checkbox') || $input.is('input:radio') ) {
        changed = (($input).prop('defaultChecked') != $input.is(':checked'));
      }
      if (changed) {
        return $input.attr('id');
      }

    }).toArray();

    if (changed_objects.length) {
      $observable_buttons.removeAttr('disabled')   
    } else {
      $observable_buttons.attr('disabled', 'disabled');
    }
  };
  touchButtons();

  $('input, textarea, select', $form).each(function () {
    var $input = $(this);

    $input.on('keyup change', function () {
      touchButtons();
    });
  });

};

Now just loop thru the forms on the page and you should see submit buttons disabled by default and they will be activated ONLY if you indeed will change some input value on the form.

$('form').each(function () {
    bindFormChange($(this));
});

Implementation as a jQuery plugin is here https://github.com/kulbida/jmodifiable

Developer
  • 983
  • 12
  • 12
2
$('form :input').change(function() {
    // Something has changed
});
VoteyDisciple
  • 37,319
  • 5
  • 97
  • 97
2
var formStr = JSON.stringify($("#form").serializeArray());
...
function Submit(){
   var newformStr = JSON.stringify($("#form").serializeArray());
   if (formStr != newformStr){
      ...
         formChangedfunct();
      ...
   }
   else {
      ...
         formUnchangedfunct();
      ...
   }
}
  • Yeah all these years later I don't even use jQuery anymore. Observers and bindings all the way now! Nice though for anyone using jQuery. – mike Jan 22 '18 at 15:36
1

Extending Udi's answer, this only checks on form submission, not on every input change.

$(document).ready( function () {
  var form_data = $('#myform').serialize();
  $('#myform').submit(function () {
      if ( form_data == $(this).serialize() ) {
        alert('no change');
      } else {
        alert('change');
      }
   });
});
Ruby Racer
  • 5,690
  • 1
  • 26
  • 43
1

You need jQuery Form Observe plugin. That's what you are looking for.

Sarfraz
  • 377,238
  • 77
  • 533
  • 578
1

$('form[name="your_form_name"] input, form[name="your_form_name"] select').click(function() {
  $("#result").html($(this).val());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Form "your_form_name"</h2>
<form name="your_form_name">
  <input type="text" name="one_a" id="one_a" value="AAAAAAAA" />
  <input type="text" name="one_b" id="one_b" value="BBBBBBBB" />
  <input type="text" name="one_c" id="one_c" value="CCCCCCCC" />
  <select name="one_d">
    <option value="111111">111111</option>
    <option value="222222">222222</option>
    <option value="333333">333333</option>
  </select>
</form>
<hr/>
<h2>Form "your_other_form_name"</h2>
<form name="your_other_form_name">
  <input type="text" name="two_a" id="two_a" value="DDDDDDDD" />
  <input type="text" name="two_b" id="two_b" value="EEEEEEEE" />
  <input type="text" name="two_c" id="two_c" value="FFFFFFFF" />
  <input type="text" name="two_d" id="two_d" value="GGGGGGGG" />
  <input type="text" name="two_e" id="two_f" value="HHHHHHHH" />
  <input type="text" name="two_f" id="two_e" value="IIIIIIII" />
  <select name="two_g">
    <option value="444444">444444</option>
    <option value="555555">555555</option>
    <option value="666666">666666</option>
  </select>
</form>
<h2>Result</h2>
<div id="result">
  <h2>Click on a field..</h2>
</div>

In addition to above @JoeD's answer.

If you want to target fields in a particular form (assuming there are more than one forms) than just fields, you can use the following code:

$('form[name="your_form_name"] input, form[name="your_form_name"] select').click(function() {
    // A form element was clicked
});
Anjana Silva
  • 8,353
  • 4
  • 51
  • 54
  • Just FYI, this probably won't do what you want - it targets the input elements of the "your_form_name" form, and all selects in the document. Also, for performance reasons, it is always better to use $.fn.find rather than just targeting descendent selectors, e.g. `$('form').find('input,select')` – dgo Jan 29 '18 at 20:35
  • @user1167442 How about if we have more than one form? My example is for a situation when there's more than one form in a single page. – Anjana Silva Jan 30 '18 at 08:49
  • 1
    But still - your example will find all the input elements that are children of the "your_form_name" form, and then find all selects whether they are children of "your_form_name" or any form at all. Your example won't work for multiple forms, unless you are only targeting the selects of those forms. Also, if you are referring to the performance piece, it is always better to use `$.fn.find` - faster than even `$.fn.filter` - to reference children rather than using descendent selectors. http://vaughnroyko.com/the-real-scoop-on-jquery-find-performance/ – dgo Jan 30 '18 at 18:02
  • 1
    @user1167442, you are absolutely right. I missed the fact that select is being targeted regardless of what form you are in. I have corrected that. Thank you very much for pointing out. Cheers! – Anjana Silva Jan 31 '18 at 09:42
1

Try this:

<script>
var form_original_data = $("form").serialize(); 
var form_submit=false;
$('[type="submit"]').click(function() {
    form_submit=true;
});
window.onbeforeunload = function() {
    //console.log($("form").submit());
    if ($("form").serialize() != form_original_data && form_submit==false) {
        return "Do you really want to leave without saving?";
    }
};
</script>
Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
0

First, I'd add a hidden input to your form to track the state of the form. Then, I'd use this jQuery snippet to set the value of the hidden input when something on the form changes:

    $("form")
    .find("input")
    .change(function(){
        if ($("#hdnFormChanged").val() == "no")
        {
            $("#hdnFormChanged").val("yes");
        }
    });

When your button is clicked, you can check the state of your hidden input:

$("#Button").click(function(){
    if($("#hdnFormChanged").val() == "yes")
    {
        // handler code here...
    }
});
JMP
  • 7,734
  • 6
  • 33
  • 34