Tengo un formulario html con pestañas. Al navegar de una pestaña a otra, los datos de la pestaña actual se conservan (en la base de datos) incluso si no hay cambios en los datos.
Me gustaría realizar la llamada de persistencia solo si se edita el formulario. El formulario puede contener cualquier tipo de control. No es necesario que ensucie el formulario escribiendo texto, pero también calificaría elegir una fecha en un control de calendario.
Una forma de lograr esto sería mostrar el formulario en modo de solo lectura de forma predeterminada y tener un botón 'Editar' y si el usuario hace clic en el botón editar, se realiza la llamada a DB (una vez más, independientemente de si se modifican los datos Esta es una mejor mejora de lo que existe actualmente).
Me gustaría saber cómo escribir una función genérica de JavaScript que verifique si se ha modificado alguno de los valores de los controles.
fuente
Respuestas:
En javascript puro, esto no sería una tarea fácil, pero jQuery lo hace muy fácil de hacer:
$("#myform :input").change(function() { $("#myform").data("changed",true); });
Luego, antes de guardar, puede verificar si se cambió:
if ($("#myform").data("changed")) { // submit the form }
En el ejemplo anterior, el formulario tiene un id igual a "myform".
Si necesita esto en muchas formas, puede convertirlo fácilmente en un complemento:
$.fn.extend({ trackChanges: function() { $(":input",this).change(function() { $(this.form).data("changed", true); }); } , isChanged: function() { return this.data("changed"); } });
Entonces puedes simplemente decir:
$("#myform").trackChanges();
y verifique si un formulario ha cambiado:
if ($("#myform").isChanged()) { // ... }
fuente
trackChanges: function () { $(document).on('change', $(this).find(':input'), function (e) { var el = $(e.target); $(el).closest('form').data("changed", true); });
En caso de que JQuery esté fuera de discusión. Una búsqueda rápida en Google encontró implementaciones de Javascript de los algoritmos hash MD5 y SHA1. Si lo desea, puede concatenar todas las entradas de formulario y hash, luego almacenar ese valor en la memoria. Cuando el usuario haya terminado. Concatenar todos los valores y volver a hacer hash. Compara los 2 hashes. Si son iguales, el usuario no cambió ningún campo del formulario. Si son diferentes, se ha editado algo y debe llamar a su código de persistencia.
fuente
No estoy seguro de haber respondido bien su pregunta, pero ¿qué pasa con addEventListener? Si no le importa demasiado el soporte de IE8, esto debería estar bien. El siguiente código me funciona:
var form = document.getElementById("myForm"); form.addEventListener("input", function () { console.log("Form has changed!"); });
fuente
Otra forma de lograr esto es serializar el formulario:
$(function() { var $form = $('form'); var initialState = $form.serialize(); $form.submit(function (e) { if (initialState === $form.serialize()) { console.log('Form is unchanged!'); } else { console.log('Form has changed!'); } e.preventDefault(); }); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <form> Field 1: <input type="text" name="field_1" value="My value 1"> <br> Field 2: <input type="text" name="field_2" value="My value 2"> <br> Check: <input type="checkbox" name="field_3" value="1"><br> <input type="submit"> </form>
fuente
Así es como lo hice (sin usar jQuery).
En mi caso, quería que no se contara un elemento de formulario en particular, porque era el elemento que activaba la verificación y, por lo tanto, siempre habrá cambiado. El elemento excepcional se llama 'reporting_period' y está codificado en la función 'hasFormChanged ()'.
Para probar, haga que un elemento llame a la función "changeReportingPeriod ()", que probablemente desee nombrar de otra manera.
IMPORTANTE: Debe llamar a setInitialValues () cuando los valores se hayan establecido en sus valores originales (generalmente al cargar la página, pero no en mi caso).
NOTA: No pretendo que esta sea una solución elegante, de hecho, no creo en las soluciones elegantes de JavaScript. Mi énfasis personal en JavaScript está en la legibilidad, no en la elegancia estructural (como si eso fuera posible en JavaScript). No me preocupo en absoluto por el tamaño del archivo cuando escribo JavaScript porque para eso es gzip, y tratar de escribir un código JavaScript más compacto conduce invariablemente a problemas intolerables con el mantenimiento. No me disculpo, no expreso ningún remordimiento y me niego a debatirlo. Es JavaScript. Lo siento, tuve que dejar esto en claro para convencerme de que debería molestarme en publicar. ¡Sea feliz! :)
var initial_values = new Array(); // Gets all form elements from the entire document. function getAllFormElements() { // Return variable. var all_form_elements = Array(); // The form. var form_activity_report = document.getElementById('form_activity_report'); // Different types of form elements. var inputs = form_activity_report.getElementsByTagName('input'); var textareas = form_activity_report.getElementsByTagName('textarea'); var selects = form_activity_report.getElementsByTagName('select'); // We do it this way because we want to return an Array, not a NodeList. var i; for (i = 0; i < inputs.length; i++) { all_form_elements.push(inputs[i]); } for (i = 0; i < textareas.length; i++) { all_form_elements.push(textareas[i]); } for (i = 0; i < selects.length; i++) { all_form_elements.push(selects[i]); } return all_form_elements; } // Sets the initial values of every form element. function setInitialFormValues() { var inputs = getAllFormElements(); for (var i = 0; i < inputs.length; i++) { initial_values.push(inputs[i].value); } } function hasFormChanged() { var has_changed = false; var elements = getAllFormElements(); for (var i = 0; i < elements.length; i++) { if (elements[i].id != 'reporting_period' && elements[i].value != initial_values[i]) { has_changed = true; break; } } return has_changed; } function changeReportingPeriod() { alert(hasFormChanged()); }
fuente
Los cambios de formulario se pueden detectar fácilmente en JavaScript nativo sin jQuery:
function initChangeDetection(form) { Array.from(form).forEach(el => el.dataset.origValue = el.value); } function formHasChanges(form) { return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value); }
initChangeDetection()
se puede llamar de forma segura varias veces a lo largo del ciclo de vida de su página: consulte Prueba en JSBinPara navegadores más antiguos que no admiten funciones de flecha / matriz más nuevas:
function initChangeDetection(form) { for (var i=0; i<form.length; i++) { var el = form[i]; el.dataset.origValue = el.value; } } function formHasChanges(form) { for (var i=0; i<form.length; i++) { var el = form[i]; if ('origValue' in el.dataset && el.dataset.origValue !== el.value) { return true; } } return false; }
fuente
Aquí hay una demostración del método polyfill en JavaScript nativo que usa la
FormData()
API para detectar entradas de formulario creadas, actualizadas y eliminadas. Puede verificar si se cambió algo usandoHTMLFormElement#isChanged
y obtener un objeto que contenga las diferencias de un formulario de reinicio usandoHTMLFormElement#changes
(asumiendo que no están enmascarados por un nombre de entrada):Object.defineProperties(HTMLFormElement.prototype, { isChanged: { configurable: true, get: function isChanged () { 'use strict' var thisData = new FormData(this) var that = this.cloneNode(true) // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset HTMLFormElement.prototype.reset.call(that) var thatData = new FormData(that) const theseKeys = Array.from(thisData.keys()) const thoseKeys = Array.from(thatData.keys()) if (theseKeys.length !== thoseKeys.length) { return true } const allKeys = new Set(theseKeys.concat(thoseKeys)) function unequal (value, index) { return value !== this[index] } for (const key of theseKeys) { const theseValues = thisData.getAll(key) const thoseValues = thatData.getAll(key) if (theseValues.length !== thoseValues.length) { return true } if (theseValues.some(unequal, thoseValues)) { return true } } return false } }, changes: { configurable: true, get: function changes () { 'use strict' var thisData = new FormData(this) var that = this.cloneNode(true) // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset HTMLFormElement.prototype.reset.call(that) var thatData = new FormData(that) const theseKeys = Array.from(thisData.keys()) const thoseKeys = Array.from(thatData.keys()) const created = new FormData() const deleted = new FormData() const updated = new FormData() const allKeys = new Set(theseKeys.concat(thoseKeys)) function unequal (value, index) { return value !== this[index] } for (const key of allKeys) { const theseValues = thisData.getAll(key) const thoseValues = thatData.getAll(key) const createdValues = theseValues.slice(thoseValues.length) const deletedValues = thoseValues.slice(theseValues.length) const minLength = Math.min(theseValues.length, thoseValues.length) const updatedValues = theseValues.slice(0, minLength).filter(unequal, thoseValues) function append (value) { this.append(key, value) } createdValues.forEach(append, created) deletedValues.forEach(append, deleted) updatedValues.forEach(append, updated) } return { created: Array.from(created), deleted: Array.from(deleted), updated: Array.from(updated) } } } }) document.querySelector('[value="Check"]').addEventListener('click', function () { if (this.form.isChanged) { console.log(this.form.changes) } else { console.log('unchanged') } })
<form> <div> <label for="name">Text Input:</label> <input type="text" name="name" id="name" value="" tabindex="1" /> </div> <div> <h4>Radio Button Choice</h4> <label for="radio-choice-1">Choice 1</label> <input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" /> <label for="radio-choice-2">Choice 2</label> <input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" /> </div> <div> <label for="select-choice">Select Dropdown Choice:</label> <select name="select-choice" id="select-choice"> <option value="Choice 1">Choice 1</option> <option value="Choice 2">Choice 2</option> <option value="Choice 3">Choice 3</option> </select> </div> <div> <label for="textarea">Textarea:</label> <textarea cols="40" rows="8" name="textarea" id="textarea"></textarea> </div> <div> <label for="checkbox">Checkbox:</label> <input type="checkbox" name="checkbox" id="checkbox" /> </div> <div> <input type="button" value="Check" /> </div> </form>
fuente