Allgemeine Methode, um festzustellen, ob das HTML-Formular bearbeitet wurde

90

Ich habe ein HTML-Formular mit Registerkarten. Beim Navigieren von einer Registerkarte zur anderen bleiben die Daten der aktuellen Registerkarte (in der Datenbank) erhalten, auch wenn keine Änderungen an den Daten vorgenommen wurden.

Ich möchte den Persistenzaufruf nur durchführen, wenn das Formular bearbeitet wird. Das Formular kann jede Art von Kontrolle enthalten. Das Verschmutzen des Formulars muss nicht durch Eingabe von Text erfolgen, sondern die Auswahl eines Datums in einem Kalendersteuerelement ist ebenfalls zulässig.

Eine Möglichkeit, dies zu erreichen, besteht darin, das Formular standardmäßig im schreibgeschützten Modus anzuzeigen und über die Schaltfläche "Bearbeiten" zu verfügen. Wenn der Benutzer auf die Schaltfläche "Bearbeiten" klickt, wird die Datenbank aufgerufen (unabhängig davon, ob Daten geändert werden) Dies ist eine bessere Verbesserung gegenüber dem, was derzeit vorhanden ist.

Ich möchte wissen, wie man eine generische Javascript-Funktion schreibt, die prüft, ob einer der Steuerelemente geändert wurde.

Sathya
quelle
Eine interessante Lösung wurde von Craig Buckler auf SitePoint veröffentlicht. Von besonderem Interesse ist, dass die Lösung nicht auf jQuery basiert und browserübergreifend kompatibel ist.
MagicAndi

Antworten:

151

In reinem Javascript wäre dies keine leichte Aufgabe, aber jQuery macht es sehr einfach:

$("#myform :input").change(function() {
   $("#myform").data("changed",true);
});

Dann können Sie vor dem Speichern überprüfen, ob es geändert wurde:

if ($("#myform").data("changed")) {
   // submit the form
}

Im obigen Beispiel hat das Formular eine ID gleich "myform".

Wenn Sie dies in vielen Formen benötigen, können Sie es einfach in ein Plugin verwandeln:

$.fn.extend({
 trackChanges: function() {
   $(":input",this).change(function() {
      $(this.form).data("changed", true);
   });
 }
 ,
 isChanged: function() { 
   return this.data("changed"); 
 }
});

Dann können Sie einfach sagen:

$("#myform").trackChanges();

und prüfen Sie, ob sich ein Formular geändert hat:

if ($("#myform").isChanged()) {
   // ...
}
Philippe Leybaert
quelle
12
Das ist schön und einfach. Wenn ein Benutzer jedoch eine Formulareingabe ändert und die Änderung dann zurücksetzt (z. B. durch zweimaliges Klicken auf ein Kontrollkästchen), wird das Formular als geändert betrachtet. Ob das akzeptabel ist oder nicht, hängt natürlich vom Kontext ab. Für eine Alternative siehe stackoverflow.com/questions/10311663/…
jlh
1
für Live-Eingaben müssen einige Änderungen vorgenommen werden trackChanges: function () { $(document).on('change', $(this).find(':input'), function (e) { var el = $(e.target); $(el).closest('form').data("changed", true); });
Dimmduh
35

Für den Fall, dass JQuery nicht in Frage kommt. Eine schnelle Suche bei Google ergab Javascript-Implementierungen von MD5- und SHA1-Hash-Algorithmen. Wenn Sie möchten, können Sie alle Formulareingaben verketten und hashen und diesen Wert dann im Speicher speichern. Wenn der Benutzer fertig ist. Verketten Sie alle Werte und den Hash erneut. Vergleichen Sie die 2 Hashes. Wenn sie identisch sind, hat der Benutzer keine Formularfelder geändert. Wenn sie unterschiedlich sind, wurde etwas bearbeitet, und Sie müssen Ihren Persistenzcode aufrufen.

Matthew Vines
quelle
2
Dies ist, was ich für diese Frage erwartet habe: Gibt es eine Bibliothek?
Hamedz
Hätte es nicht das gleiche Ergebnis, wenn Sie alle Felder verkettet hätten, es aber nicht gehasht hätten?
Ashleedawg
Ja, aber die Daten selbst sind möglicherweise größer als der Hash.
JRG
23

Ich bin nicht sicher, ob ich Ihre Frage richtig verstehe, aber was ist mit addEventListener? Wenn Sie sich nicht zu sehr für die IE8-Unterstützung interessieren, sollte dies in Ordnung sein. Der folgende Code funktioniert für mich:

var form = document.getElementById("myForm");

form.addEventListener("input", function () {
    console.log("Form has changed!");
});
Mecograph
quelle
Perfekt für meine Bedürfnisse
Segen
7

Eine andere Möglichkeit, dies zu erreichen, besteht darin, das Formular zu serialisieren:

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

Nikoskip
quelle
Mir gefällt, wie Sie denken, besonders wenn Sie die jQuery herausreißen und dies mit Vanille-JavaScript tun. Mir ist klar, dass die Frage angenommen hat, dass jQuery verfügbar ist, aber kein Grund, keine noch umfassendere Lösung zu haben, ähnlich wie bei den Vorbehalten in den Kommentaren.
Ruffin
4

So habe ich es gemacht (ohne jQuery zu verwenden).

In meinem Fall wollte ich, dass ein bestimmtes Formularelement nicht gezählt wird, da es das Element war, das die Prüfung ausgelöst hat und sich daher immer geändert hat. Das außergewöhnliche Element heißt 'reporting_period' und ist in der Funktion 'hasFormChanged ()' fest codiert.

Lassen Sie zum Testen ein Element die Funktion "changeReportingPeriod ()" aufrufen, die Sie wahrscheinlich etwas anderes benennen möchten.

WICHTIG: Sie müssen setInitialValues ​​() aufrufen, wenn die Werte auf ihre ursprünglichen Werte gesetzt wurden (normalerweise beim Laden der Seite, in meinem Fall jedoch nicht).

HINWEIS: Ich behaupte nicht, dass dies eine elegante Lösung ist. Tatsächlich glaube ich nicht an elegante JavaScript-Lösungen. Mein persönlicher Schwerpunkt in JavaScript liegt auf der Lesbarkeit, nicht auf struktureller Eleganz (als ob dies in JavaScript möglich wäre). Ich beschäftige mich beim Schreiben von JavaScript überhaupt nicht mit der Dateigröße, da dies der Zweck von gzip ist und der Versuch, kompakteren JavaScript-Code zu schreiben, ausnahmslos zu unerträglichen Problemen bei der Wartung führt. Ich entschuldige mich nicht, drücke keine Reue aus und lehne es ab, darüber zu debattieren. Es ist JavaScript. Entschuldigung, ich musste dies klarstellen, um mich davon zu überzeugen, dass ich mir die Mühe machen sollte, etwas zu posten. Sei glücklich! :) :)


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

Teekin
quelle
3

Formularänderungen können in nativem JavaScript ohne jQuery leicht erkannt werden:

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()kann während des gesamten Lebenszyklus Ihrer Seite sicher mehrmals aufgerufen werden: Siehe Test auf JSBin


Für ältere Browser, die neuere Pfeil- / Array-Funktionen nicht unterstützen:

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;
}
AnthumChris
quelle
gibt immer wahr für meine Form zurück
Rich Stone
Könnten Sie Ihr Formular auf JSBin oder einem Gist veröffentlichen, damit wir Ihr Formular sehen können?
AnthumChris
Mein Fehler, ich habe das Formular mit jquery und nicht mit JS wie in Ihrem Beispiel ausgewählt. Es ist eine schöne und saubere js-Lösung ohne Serialisierungsüberlastung. Danke dir!
Rich Stone
2

Hier ist eine Demo der Polyfill-Methode in nativem JavaScript, die mithilfe der FormData()API erstellte, aktualisierte und gelöschte Formulareinträge erkennt. Sie können überprüfen, ob etwas geändert wurde, indem HTMLFormElement#isChangedSie ein Objekt mit den Unterschieden zu einem zurückgesetzten Formular abrufen HTMLFormElement#changes(vorausgesetzt, sie werden nicht durch einen Eingabenamen maskiert):

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>

Patrick Roberts
quelle