¿Cómo se maneja un cambio de formulario en jQuery?

81

En jQuery, ¿hay una forma sencilla de probar si ALGUNO de los elementos de un formulario ha cambiado?

EDITAR: Debería haber agregado que solo necesito verificar un click()evento.

EDITAR: ¡ Lo siento, debería haber sido más específico! Digamos que tengo un formulario y tengo un botón con lo siguiente:

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

¿Cómo probaría si el formulario ha cambiado desde que se cargó?

Miguel
fuente
¿Te refieres a la acción de algún otro script incluido en la página o tan pronto como el usuario haya escrito algún texto o haya hecho clic en una radio / casilla de verificación?
FelipeAls
Debería haber agregado que solo necesito verificar un evento click ()
Mike

Respuestas:

140

Puedes hacerlo:

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

Esto adapta un changecontrolador de eventos a las entradas en el formulario, si alguno de ellos cambia lo que usa .data()para establecer un changedvalor true, entonces simplemente verificamos ese valor en el clic, esto supone que #mybuttonestá dentro del formulario (si no solo lo reemplaza $(this).closest('form')con $('#myForm')), pero podrías hacerlo aún más genérico, así:

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

Referencias: actualizado

Según jQuery, este es un filtro para seleccionar todos los controles de formulario.

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

El selector de entrada: básicamente selecciona todos los controles de formulario.

Nick Craver
fuente
6
¿Tiene en $('form :input')cuenta la selección, el área de texto y otros tipos de entrada? ¿O es específico de la etiqueta de entrada?
Val
6
Corrígeme si me equivoco, pero parece que este método dirá que el formulario ha sido cambiado, incluso si alguien realiza un cambio y luego "deshace" su cambio, volviendo manualmente a los valores originales en el formulario. En este caso, la forma realmente no habría cambiado con respecto a los datos, pero este método diría que sí.
paul
1
Gracias, sí lo intenté (aparece en otra respuesta) pero no funcionó para mí. Parece que hay algunos casos especiales en los que ese método también se dispara. Actualmente trabajando con un complemento - github.com/codedance/jquery.AreYouSure
paul
1
¡Buena solución para este caso! Pero si alguien aquí quiere obtener el estado del contenido del formulario (como "IsDirty"), es posible que desee seguir esta solución: stackoverflow.com/a/26796840/665783
Jacob
1
@Nick Craver: ¿Qué pasa si el elemento de formulario está fuera de la etiqueta de formulario con el atributo de formulario como id del formulario? He probado el caso, mala suerte, no funciona.
Mahantesh
49

Si desea verificar si los datos del formulario, ya que se enviarán al servidor, han cambiado, puede serializar los datos del formulario al cargar la página y compararlos con los datos del formulario actual:

$(function() {

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

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

});
Udi
fuente
5
Excelente, esta es la mejor solución aquí. Gracias.
Anónimo
3
Usé este método una vez, y se volvió muy lento cuando la forma se hizo más grande. Este es un buen método para formularios pequeños, pero no es el camino a seguir para formularios grandes.
mkdevoogd
¿te refieres a formularios con archivos en ellos?
Udi
1
Esto es bueno, pero sería mejor si en lugar de verificar cada vez que cambia un control, verifique directamente cuándo está a punto de enviarse el formulario
Ruby Racer
2
Esto es mucho mejor, por lo que puede activar los botones para volver a desactivarlos, como "eliminar"
EliuX
29

Una solución simple y en tiempo real:

$('form').on('keyup change paste', 'input, select, textarea', function(){
    console.log('Form changed!');
});
Marcio Mazzucato
fuente
1
Muy buena solucion. Adecuado para la validación de formularios mientras escribe.
Bijan
1
¡La mejor solución para mí! Muchas gracias.
Ivan
2
Considere usar en 'input'lugar de 'keyup change'manejar el pegado con el botón derecho del mouse, a menos que necesite admitir navegadores más antiguos.
TrueWill
10

Puede utilizar varios selectores para adjuntar una devolución de llamada al evento de cambio para cualquier elemento de formulario.

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

EDITAR

Como mencionó que solo necesita esto para un clic, simplemente puede modificar mi código original a esto:

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

EDITAR # 2

Ok, puede establecer un global que se establece una vez que se ha cambiado algo así:

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
fuente
3

Mirando la pregunta actualizada, intente algo como

$('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");
    });
});

Para la pregunta original, use algo como

$('input').change(function() {
    alert("Things have changed!");
});
Pez Cuckow
fuente
Esto no marca <textarea>ni <select>elementos.
Nick Craver
Después de su edición ... esto parece una gran cantidad de trabajo adicional y la transferencia de datos, todo lo que necesita es un booleanajuste a verdadero cuando ocurre un cambio. Además, esto todavía no es específico de un formulario, ¿y si hubiera otros formularios en la página?
Nick Craver
+1 eso es bueno, pero necesita algo de trabajo, vincúlelo para formar y configure init.
Anónimo
eso es exactamente lo que pensé :) (y) estás rockeado
RK Sharma
2
$('form :input').change(function() {
    // Something has changed
});
VoteyDisciple
fuente
2

Aquí tienes una elegante solución.

Existe una propiedad oculta para cada elemento de entrada en el formulario que puede usar para determinar si se cambió o no el valor. Cada tipo de entrada tiene su propio nombre de propiedad. Por ejemplo

  • porque text/textareaes defaultValue
  • porque selectes predeterminadoSelect
  • porque checkbox/radioes predeterminado

Aquí está el ejemplo.

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();
    });
  });

};

Ahora simplemente recorra los formularios en la página y debería ver los botones de envío deshabilitados por defecto y se activarán SOLAMENTE si realmente cambia algún valor de entrada en el formulario.

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

La implementación como jQuerycomplemento está aquí https://github.com/kulbida/jmodifiable

Desarrollador
fuente
2
var formStr = JSON.stringify($("#form").serializeArray());
...
function Submit(){
   var newformStr = JSON.stringify($("#form").serializeArray());
   if (formStr != newformStr){
      ...
         formChangedfunct();
      ...
   }
   else {
      ...
         formUnchangedfunct();
      ...
   }
}
Péter Tajti
fuente
Sí, todos estos años después, ya ni siquiera uso jQuery. ¡Observadores y ataduras todo el camino ahora! Aunque es bueno para cualquiera que use jQuery.
Mike
1

Extendiendo la respuesta de Udi, esto solo verifica el envío del formulario, no cada cambio de entrada.

$(document).ready( function () {
  var form_data = $('#myform').serialize();
  $('#myform').submit(function () {
      if ( form_data == $(this).serialize() ) {
        alert('no change');
      } else {
        alert('change');
      }
   });
});
Ruby Racer
fuente
0

Primero, agregaría una entrada oculta a su formulario para rastrear el estado del formulario. Luego, usaría este fragmento de jQuery para establecer el valor de la entrada oculta cuando algo en el formulario cambia:

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

Cuando se hace clic en su botón, puede verificar el estado de su entrada oculta:

$("#Button").click(function(){
    if($("#hdnFormChanged").val() == "yes")
    {
        // handler code here...
    }
});
JMP
fuente
0

$('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>

Además de la respuesta anterior de @ JoeD.

Si desea apuntar a campos en un formulario en particular (asumiendo que hay más de un formulario) que solo campos, puede usar el siguiente código:

$('form[name="your_form_name"] input, form[name="your_form_name"] select').click(function() {
    // A form element was clicked
});
Anjana Silva
fuente
Solo para su información, esto probablemente no hará lo que usted desea: apunta a los elementos de entrada del formulario "your_form_name" y todas las selecciones en el documento. Además, por razones de rendimiento, siempre es mejor usar $ .fn.find en lugar de apuntar solo a selectores descendientes, por ejemplo$('form').find('input,select')
dgo
@ user1167442 ¿Qué tal si tenemos más de un formulario? Mi ejemplo es para una situación en la que hay más de un formulario en una sola página.
Anjana Silva
1
Pero aún así, su ejemplo encontrará todos los elementos de entrada que son hijos del formulario "your_form_name", y luego encontrará todas las selecciones si son hijos de "your_form_name" o cualquier formulario. Su ejemplo no funcionará para varios formularios, a menos que solo se oriente a las selecciones de esos formularios. Además, si se refiere a la pieza de rendimiento, siempre es mejor usar $.fn.find, más rápido que incluso $.fn.filter, para hacer referencia a niños en lugar de usar selectores descendientes. vaughnroyko.com/the-real-scoop-on-jquery-find-performance
dgo
1
@ user1167442, tienes toda la razón. Me perdí el hecho de que seleccionar está siendo el objetivo, independientemente de la forma en la que se encuentre. Lo he corregido. Muchas gracias por señalarlo. ¡Salud!
Anjana Silva
0

Prueba esto:

<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>
Vinod saraswat
fuente