2008-10-11 15 views
41

Me gustaría hacer un seguimiento de los cambios en las entradas en un formulario a través de javascript. Mi intención es (pero no limitado) a¿Cuál es la mejor manera de rastrear los cambios en un formulario a través de javascript?

  • permitan botón "Guardar" sólo cuando algo ha cambiado
  • alerta si el usuario quiere cerrar la página y algo no está salvado

ideas?

+1

Sé que tengo 5 años de retraso, pero creo que puedo mejorar las soluciones anteriores ... Acabo de corregir y actualizar mi respuesta a continuación. – skibulk

Respuesta

28

Loop a través de todos los elementos de entrada, y se puso un controlador de onchange en cada uno. Cuando eso ocurra, configure una bandera que le permita saber que el formulario ha cambiado. Una versión básica de eso sería muy fácil de configurar, pero no sería lo suficientemente inteligente como para reconocer si alguien ha cambiado una entrada de "a" a "b" y luego vuelve a "a". Si fuera importante atrapar ese caso, aún sería posible, pero tomaría un poco más de trabajo.

Aquí hay un ejemplo básico de jQuery:

$("#myForm") 
    .on("change", ":input", function() { 
     // do whatever you need to do when something's changed. 
     // perhaps set up an onExit function on the window 
     $('#saveButton').show(); 
    }) 
; 
+3

Puede usar un cierre en su bucle para almacenar el valor del campo de entrada, y luego puede probar para ver si la entrada cambió nuevamente al valor. Sin embargo, tendrás que hacer algunos casos especiales para botones de radio, casillas de verificación y el botón de enviar. ¡OH, y no te olvides de eliminar el desvinculación en este caso! –

+1

AFAIK, el evento de cambio solo se desencadena, p. una entrada de texto si el contenido se cambia y _la entrada pierde el foco_. Esto significa que el botón de guardar no está habilitado hasta que el usuario p. Ej. golpea la pestaña más o menos, ¿no? – ssc

+0

@ssc, sí, eso es correcto. Sin embargo, la mayoría de las formas probablemente tengan más de un campo. – nickf

15

elementos de formulario de texto en JS exponen un .value propiedad y una propiedad .defaultValue, por lo que se pueden implementar fácilmente algo como:

function formChanged(form) { 
    for (var i = 0; i < form.elements.length; i++) { 
     if(form.elements[i].value != form.elements[i].defaultValue) return(true); 
    } 
    return(false); 
} 

Para casillas de verificación y botones de radio ver si element.checked != element.defaultChecked, y para HTML <select /> elementos que necesitará para recorrer la matriz select.options y comprobar para cada opción si selected == defaultSelected.

Es posible que desee utilizar un marco como jQuery para adjuntar controladores al evento onchange de cada elemento de formulario individual. Estos controladores pueden llamar a su código formChanged() y modificar la propiedad enabled de su botón "Guardar", y/o montar/desmontar un controlador de eventos para el evento del cuerpo del documento beforeunload.

0

conectar un controlador de eventos para cada forma de entrada/evento onchange de selección/del área de texto. Establecer una variable para decirle si debe habilitar el botón "guardar". Crear un lanzador onunload que los controles de una forma demasiado sucio, y cuando se envía el formulario restablecer la variable:

 
window.onunload = checkUnsavedPage; 
var isDirty = false; 
var formElements = //Get a reference to all form elements 
for(var i = 0; len = formElements.length; i++) { 
    //Add onchange event to each element to call formChanged() 
} 

function formChanged(event) { 
    isDirty = false; 
    document.getElementById("savebtn").disabled = ""; 
} 

function checkUnsavedPage() { 
    if (isDirty) { 
     var isSure = confirm("you sure?"); 
     if (!isSure) { 
      event.preventDefault(); 
     } 
    } 
} 
1

para alertar al usuario antes de cerrar, el uso unbeforeunload:

window.onbeforeunload = function() { 
    return "You are about to lose your form data."; 
}; 
+0

Simplemente devuelva el cadena. El navegador se estafará firma() it. – eyelidlessness

2

respondí una pregunta así en Ars Technica, pero la pregunta se enmarcó de tal forma que los cambios debían detectarse incluso si el usuario no difumina un campo de texto (en cuyo caso el evento de cambio nunca se dispara).Se me ocurrió con un guión completo que:

  1. permite enviar y botones de reinicio si los valores del campo cambiará
  2. desactiva envío y de reinicialización botones si la forma se restablece
  3. interrupciones que salen de la página si el formulario de datos ha cambiado y no se ha presentado
  4. soporta IE 6+, Firefox 2+, Safari 3+ (y, presumiblemente, Opera, pero no he probado)

Este script depende de prototipo, pero podría adaptarse fácilmente a otro libr ario o para estar solo.

$(document).observe('dom:loaded', function(e) { 
    var browser = { 
     trident: !!document.all && !window.opera, 
     webkit: (!(!!document.all && !window.opera) && !document.doctype) || 
      (!!window.devicePixelRatio && !!window.getMatchedCSSRules) 
    }; 

    // Select form elements that won't bubble up delegated events (eg. onchange) 
    var inputs = $('form_id').select('select, input[type="radio"], input[type="checkbox"]'); 

    $('form_id').observe('submit', function(e) { 
     // Don't bother submitting if form not modified 
     if(!$('form_id').hasClassName('modified')) { 
      e.stop(); 
      return false; 
     } 
     $('form_id').addClassName('saving'); 
    }); 

    var change = function(e) { 
     // Paste event fires before content has been pasted 
     if(e && e.type && e.type == 'paste') { 
      arguments.callee.defer(); 
      return false; 
     } 

     // Check if event actually results in changed data 
     if(!e || e.type != 'change') { 
      var modified = false; 
      $('form_id').getElements().each(function(element) { 
       if(element.tagName.match(/^textarea$/i)) { 
        if($F(element) != element.defaultValue) { 
         modified = true; 
        } 
        return; 
       } else if(element.tagName.match(/^input$/i)) { 
        if(element.type.match(/^(text|hidden)$/i) && $F(element) != element.defaultValue) { 
         modified = true; 
        } else if(element.type.match(/^(checkbox|radio)$/i) && element.checked != element.defaultChecked) { 
         modified = true; 
        } 
       } 
      }); 
      if(!modified) { 
       return false; 
      } 
     } 

     // Mark form as modified 
     $('form_id').addClassName('modified'); 

     // Enable submit/reset buttons 
     $('reset_button_id').removeAttribute('disabled'); 
     $('submit_button_id').removeAttribute('disabled'); 

     // Remove event handlers as they're no longer needed 
     if(browser.trident) { 
      $('form_id').stopObserving('keyup', change); 
      $('form_id').stopObserving('paste', change); 
     } else { 
      $('form_id').stopObserving('input', change); 
     } 
     if(browser.webkit) { 
      $$('#form_id textarea').invoke('stopObserving', 'keyup', change); 
      $$('#form_id textarea').invoke('stopObserving', 'paste', change); 
     } 
     inputs.invoke('stopObserving', 'change', arguments.callee); 
    }; 

    $('form_id').observe('reset', function(e) { 
     // Unset form modified, restart modified check... 
     $('reset_button_id').writeAttribute('disabled', true); 
     $('submit_button_id').writeAttribute('disabled', true); 
     $('form_id').removeClassName('modified'); 
     startObservers(); 
    }); 

    var startObservers = (function(e) { 
     if(browser.trident) { 
      $('form_id').observe('keyup', change); 
      $('form_id').observe('paste', change); 
     } else { 
      $('form_id').observe('input', change); 
     } 
     // Webkit apparently doesn't fire oninput in textareas 
     if(browser.webkit) { 
      $$('#form_id textarea').invoke('observe', 'keyup', change); 
      $$('#form_id textarea').invoke('observe', 'paste', change); 
     } 
     inputs.invoke('observe', 'change', change); 
     return arguments.callee; 
    })(); 

    window.onbeforeunload = function(e) { 
     if($('form_id').hasClassName('modified') && !$('form_id').hasClassName('saving')) { 
      return 'You have unsaved content, would you really like to leave the page? All your changes will be lost.'; 
     } 
    }; 

}); 
+0

si el usuario no difumina un campo de texto modificado: onkeyup() y almacena el valor modificado. – Ber

1

Guardaría cada valor de los campos en una variable cuando se cargue la página, luego compararé esos valores cuando el usuario descargue la página. Si se detectan diferencias, sabrá qué guardar y, aún mejor, podrá decirle específicamente al usuario qué datos no se guardarán si salen.

// this example uses the prototype library 
// also, it's not very efficient, I just threw it together 
var valuesAtLoad = []; 
var valuesOnCheck = []; 
var isDirty = false; 
var names = []; 
Event.observe(window, 'load', function() { 
    $$('.field').each(function(i) { 
     valuesAtLoad.push($F(i)); 
    }); 
}); 

var checkValues = function() { 
    var changes = []; 
    valuesOnCheck = []; 
    $$('.field').each(function(i) { 
     valuesOnCheck.push($F(i)); 
    }); 

    for(var i = 0; i <= valuesOnCheck.length - 1; i++) { 
     var source = valuesOnCheck[i]; 
     var compare = valuesAtLoad[i]; 
     if(source !== compare) { 
      changes.push($$('.field')[i]); 
     } 
    } 

    return changes.length > 0 ? changes : []; 
}; 

setInterval(function() { names = checkValues().pluck('id'); isDirty = names.length > 0; }, 100); 

// notify the user when they exit 
Event.observe(window, 'beforeunload', function(e) { 
    e.returnValue = isDirty ? "you have changed the following fields: \r\n" + names + "\r\n these changes will be lost if you exit. Are you sure you want to continue?" : true; 
}); 
0

Aquí es una implementación completa de Dylan Beattie's suggestion:

Client/JS Framework for "Unsaved Data" Protection?

No debería ser necesario para almacenar los valores iniciales para determinar si la forma ha cambiado, a menos que estés poblar dinámicamente en el cliente lado (aunque, incluso entonces, aún podría configurar las propiedades default en los elementos del formulario).

5

Trate

function isModifiedForm(form){ 
    var __clone = $(form).clone(); 
    __clone[0].reset(); 
    return $(form).serialize() == $(__clone).serialize(); 
} 

Espero su ayuda))

4

Aquí es un método & jQuery JavaScript para detectar cambios en el formulario que es simple. Deshabilita el botón de enviar hasta que se realicen cambios. Detecta intentos de abandonar la página por otros medios que no sean enviar el formulario. Representa "undos" por parte del usuario, está encapsulado dentro de una función para facilitar la aplicación y no falla al enviar. Simplemente llame a la función y pase la identificación de su formulario.

Esta función serializa el formulario una vez cuando se carga la página, y nuevamente antes de que el usuario abandone la página. Si los dos estados de formulario son diferentes, se muestra el mensaje.

Pruébelo: http://jsfiddle.net/skibulk/ev5rE/

function formUnloadPrompt(formSelector) { 
    var formA = $(formSelector).serialize(), formB, formSubmit = false; 

    // Detect Form Submit 
    $(formSelector).submit(function(){ 
     formSubmit = true; 
    }); 

    // Handle Form Unload  
    window.onbeforeunload = function(){ 
     if (formSubmit) return; 
     formB = $(formSelector).serialize(); 
     if (formA != formB) return "Your changes have not been saved."; 
    }; 

    // Enable & Disable Submit Button 
    var formToggleSubmit = function(){ 
     formB = $(formSelector).serialize(); 
     $(formSelector+' [type="submit"]').attr("disabled", formA == formB); 
    }; 

    formToggleSubmit(); 
    $(formSelector).change(formToggleSubmit); 
    $(formSelector).keyup(formToggleSubmit); 
} 



// Call function on DOM Ready: 

$(function(){ 
    formUnloadPrompt('form'); 
}); 
0

que tenían el mismo desafío y estaba pensando en una solución común.El siguiente código no es perfecto, es desde la inicial r & d. Los siguientes son los pasos que seguí:

1) Mueva los siguientes JS a un otro archivo (por ejemplo changeFramework.js)

2) Incluir en su proyecto mediante la importación

3) En su html página, cualquiera que sea el control que necesite monitoreo, agregue la clase "monitorChange"

4) La variable global 'hasChanged' indicará, si hay algún cambio en la página en la que está trabajando.

<script type="text/javascript" id="MonitorChangeFramework"> 
// MONITOR CHANGE FRAMEWORK 
// ALL ELEMENTS WITH CLASS ".monitorChange" WILL BE REGISTERED FOR CHANGE 
// ON CHANGE IT WILL RAISE A FLAG 
var hasChanged; 

function MonitorChange() { 
    hasChanged = false; 
    $(".monitorChange").change(function() { 
     hasChanged = true; 
    }); 
} 

Los siguientes son los controles donde solía este marco:

<textarea class="monitorChange" rows="5" cols="10" id="testArea"></textarea></br> 
     <div id="divDrinks"> 
      <input type="checkbox" class="chb monitorChange" value="Tea" />Tea </br> 
      <input type="checkbox" class="chb monitorChange" value="Milk" checked='checked' />Milk</br> 
      <input type="checkbox" class="chb monitorChange" value="Coffee" />Coffee </br> 
     </div> 
     <select id="comboCar" class="monitorChange"> 
      <option value="volvo">Volvo</option> 
      <option value="saab">Saab</option> 
      <option value="mercedes">Mercedes</option> 
      <option value="audi">Audi</option> 
     </select> 
     <button id="testButton"> 
      test</button><a onclick="NavigateTo()">next >>> </a> 

Creo que no puede ser enorme mejora en este marco. Comentarios/Cambios/retroalimentaciones son bienvenidos. :)

0

Hice algunas pruebas del navegador cruzado.

En Chrome y Safari esto es agradable:

<form onchange="validate()"> 
... 
</form> 

Para Firefox + Chrome/Safari voy con esto:

<form onkeydown="validate()"> 
    ... 
    <input type="checkbox" onchange="validate()"> 
</form> 

Artículos como casillas de verificación o botones de radio necesitan un detector de eventos onchange propia.

Cuestiones relacionadas