30

I'm using ASP.NET 2.0 with a Master Page, and I was wondering if anyone knew of a way to detect when the fields within a certain <div> or fieldset have been changed (e.g., marked 'IsDirty')?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AlteredConcept
  • 2,602
  • 7
  • 32
  • 36

10 Answers10

55

You could bind the Change event for all inputs and flag a variable as true. Like this.

var somethingChanged = false;
$(document).ready(function() { 
   $('input').change(function() { 
        somethingChanged = true; 
   }); 
});

But, keep in mind that if the user changes something, then changes back to the original values, it will still be flagged as changed.

UPDATE: For a specific div or fieldset. Just use the id for the given fieldset or div. Example:

var somethingChanged = false;
$(document).ready(function() { 
   $('#myDiv input').change(function() { 
        somethingChanged = true; 
   }); 
});
Jose Basilio
  • 50,714
  • 13
  • 121
  • 117
32

Quick (but very dirty) solution

This is quick, but it won't take care of ctrl+z or cmd+z and it will give you a false positive when pressing shift, ctrl or the tab key:

$('#my-form').on('change keyup paste', ':input', function(e) {
    // The form has been changed. Your code here.
});

Test it with this fiddle.


Quick (less dirty) solution

This will prevent false positives for shift, ctrl or the tab key, but it won't handle ctrl+z or cmd+z:

$('#my-form').on('change keyup paste', ':input', function(e) {

  var keycode = e.which;

  if (e.type === 'paste' || e.type === 'change' || (
      (keycode === 46 || keycode === 8) || // delete & backspace
      (keycode > 47 && keycode < 58) || // number keys
      keycode == 32 || keycode == 13 || // spacebar & return key(s) (if you want to allow carriage returns)
      (keycode > 64 && keycode < 91) || // letter keys
      (keycode > 95 && keycode < 112) || // numpad keys
      (keycode > 185 && keycode < 193) || // ;=,-./` (in order)
      (keycode > 218 && keycode < 223))) { // [\]' (in order))

    // The form has been changed. Your code here.

  }

});

Test it with this fiddle.


A complete solution

If you want to handle all the cases, you should use:

// init the form when the document is ready or when the form is populated after an ajax call
$(document).ready(function() {
  $('#my-form').find(':input').each(function(index, value) {
    $(this).data('val', $(this).val());
  });
})

$('#my-form').on('change paste', ':input', function(e) {
  $(this).data('val', $(this).val());
  // The form has been changed. Your code here.
});

$('#my-form').on('keyup', ':input', function(e) {
  if ($(this).val() != $(this).data('val')) {
    $(this).data('val', $(this).val());
    // The form has been changed. Your code here. 
  }
});

Test it with this fiddle.

Luca Fagioli
  • 12,722
  • 5
  • 59
  • 57
28

A simple and elegant solution (it detects form elements changes in real time):

var formChanged = false;

$('#my-div form').on('keyup change paste', 'input, select, textarea', function(){
    formChanged = true;
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marcio Mazzucato
  • 8,841
  • 9
  • 64
  • 79
  • 3
    Love it. I replaced `'input, select, textarea'` with `:input` for simplicity though ;) – Joshua Burns Jul 21 '16 at 18:06
  • @Marcio Mazzucato May you tell me why do you need event 'keyup' over here? Thank you for your explanation. – Thomas.Benz Nov 12 '17 at 17:08
  • @Thomas.Benz, Sure! Because `.change()` event fires only when selector has lost focus. You can see more info [in this answer](https://stackoverflow.com/a/1443299/999820). – Marcio Mazzucato Nov 12 '17 at 18:59
  • @Marcio Mazzucato . Thank you for your explanation. By the way, I think your code can be made more simplified using selector ':input'. So 'input, select, textarea' can be replaced by ':input' as Joshua Burns mentioned. About :input Selector can be seen at http://api.jquery.com/input-selector/ – Thomas.Benz Nov 13 '17 at 12:55
  • @Marcio Mazzucato I have one more question: if for some text box or text area, if user only use the mouse to copy and paste text, it seems the detector method is not fired until the text box or text area loses focus. So how can deal with that situation? Maybe some mouse events are needed to as in the event selector besides 'keyup' and 'change'? – Thomas.Benz Nov 13 '17 at 13:31
  • 1
    @Thomas.Benz, I am not using `:input` only for an easier readability, but you can implement it. For the case you mentioned, you can use the `paste` event, i updated the answer right now, thanks! – Marcio Mazzucato Nov 13 '17 at 20:14
  • 1
    @Marcio Mazzucato Perfect. Now "the mouse copying and pasting data also making form input changes" is included. – Thomas.Benz Nov 19 '17 at 13:54
  • **Warning** This approach will give you a false positive simply by pressing `shift`, `ctrl`, the `tab` key or any other non-printable character, which is wrong. Check it with [this fiddle](https://jsfiddle.net/fu9vaxwL/61/). Check [my answer](https://stackoverflow.com/a/11795226/636561) for the solution. – Luca Fagioli Jul 28 '18 at 09:08
12

For a form you could serialize the contents on load then compare serialization at a later time, e.g.:

$(function(){
    var initdata=$('form').serialize();
    $('form').submit(function(e){
        e.preventDefault();
        var nowdata=$('form').serialize();
        if(initdata==nowdata) console.log('nothing changed'); else console.log('something changed');
        // save
        initdata=nowdata;
        $.post('settings.php',nowdata).done(function(){
            console.log('saved');
        });
    });
});

Note this requires form elements to have a name attribute.

dw1
  • 1,495
  • 13
  • 15
  • 2
    Thanks for this! Your solution is extremely elegant, simple and works with forms containing arrays of values. – Todd Hammer Jan 24 '19 at 14:44
5

Just to clarify because the question is "within a certain fieldset/div":

var somethingChanged = false;
$(document).ready(function() { 
   $('fieldset > input').change(function() { 
        somethingChanged = true; 
   }); 
});

or

var somethingChanged = false;
$(document).ready(function() { 
   $('div > input').change(function() { 
        somethingChanged = true; 
   }); 
});
RedWolves
  • 10,379
  • 12
  • 49
  • 68
4

You can give the fieldset or div an ID and bind the change event to it ... the event should propagate from the inner children.

var somethingChanged = false;
$('#fieldset_id').change(function(e)
{
    // e.target is the element which triggered the event
    // console.log(e.target);
    somethingChanged = true;
});

Additionally if you wanted to have a single event listening function you could put the change event on the form and then check which fieldset changed:

$('#form_id').change(function(e)
{
    var changedFieldset = $(e.target).parents('fieldset');
    // do stuff
});
farinspace
  • 8,422
  • 6
  • 33
  • 46
  • good article on JS event delegation ... http://www.sitepoint.com/blogs/2008/07/23/javascript-event-delegation-is-easier-than-you-think/ – farinspace May 05 '09 at 01:42
  • That's the best solution. Thank you! Although it still ignores when input values change dinamically. – David Gras Nov 26 '14 at 18:31
2

I came up with this piece of code in CoffeeScript (not really field tested, yet):

  • Add class 'change_warning' to forms that should be watched for changes.

  • Add class 'change_allowed' to the save button.

change_warning.coffee:

window.ChangeWarning = {
    save: ->
        $(".change_warning").each (index,element) ->
            $(element).data('serialized', $(element).serialize())

    changed: (element) ->
        $(element).serialize() != $(element).data('serialized')

    changed_any: ->
        $.makeArray($(".change_warning").map (index,element) -> ChangeWarning.changed(element)).some (f)->f
        # AKA $(".change_warning").any (element) -> ChangeWarning.changed(element)
        # But jQuery collections do not know the any/some method, yet (nor are they arrays)

    change_allowed: ->
        ChangeWarning.change_allowed_flag = true

    beforeunload: ->
        unless ChangeWarning.change_allowed_flag or not ChangeWarning.changed_any()
            "You have unsaved changes"
}

$ ->
    ChangeWarning.save()
    $(".change_allowed").bind 'click', -> ChangeWarning.change_allowed()
    $(window).bind 'beforeunload',     -> ChangeWarning.beforeunload()
Community
  • 1
  • 1
edx
  • 1,317
  • 1
  • 12
  • 14
1

An alternative to Dw7's answer if you only want the fields inside a fieldset then you can call serialize() on its input values. Note: serialize() will not pickup any elements that do not have a "name" attribute. This will work for select tags as well.

var initialValues = $('#your-fieldset :input').serialize();

$('form').submit(function(e) {
  e.preventDefault();
  var currentValues = $('#your-fieldset :input').serialize();
  if (currentValues == initialValues) {
    // Nothing has changed
    alert('Nothing was changed');
  }
  else {
    this.submit();
  }
});

0

.live is now deprecated and replaced by .on:

var confirmerSortie = false;
$(document).on('change', 'select', function() {
    confirmerSortie = true;
});

$(document).on('change keypress', 'input', function() {
    confirmerSortie = true;
});

$(document).on('change keypress', 'textarea', function() {
    confirmerSortie = true;
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
André DLC
  • 261
  • 4
  • 11
0

The following solution worked for me:

$("#myDiv :input").change(function() { $("#myDiv").data("changed",true);});
}
  
if($("#myDiv").data("changed")) {
console.log('Form data changed hence proceed to submit');  
}
else {
console.log('No change detected!');
}

Thanks

Tieson T.
  • 20,774
  • 6
  • 77
  • 92
Hasan K
  • 610
  • 8
  • 20