Warnung bei ungespeicherten Änderungen

Ich arbeite normalerweise viel mit jQuery, was neben der an sich schon tollen Library den Vorteil hat, dass es für viele Probleme sehr kleine und gute Plugins gibt. Aktuell arbeite ich an einem Projekt, das ein etwas älteres Formular-Framework sowie als JavaScript-Bibliothek Prototype einsetzt. Das Problem, dass sich stellte: Dem Nutzer eine Warnung anzeigen, wenn er in ein Formularfeld etwas eingetragen hat und die Seite verlässt, ohne diese Änderung zu speichern. Mit jQuery würde man dazu ja z.B. das DirtyForm-Plugin nehmen, für Prototype habe ich dagegen keine fertige Lösung gefunden. Das folgende ist relativ überschaubar, funktioniert soweit ich das sagen kann aber sehr schön. Es verlässt sich natürlich auf das Vorhandensein von JavaScript im Browser, für Nutzer ohne JS kann man in der Hinsicht nicht viel tun.

Grob skizziert sieht die Lösung so aus: Beim Laden der Seite geht das Script das Formular durch und speichert den Zustand in einer globalen Variable ab. Verlässt man die Seite, wird über den Event-Handler „onbeforeunload“ das Formular auf Unterschiede zum Anfangszustand geprüft. Falls es keine gibt, kann der Nutzer gehen, ansonsten wird er gefragt, ob er die Seite wirklich verlassen möchte. Bei einem Submit des Formulars wird das natürlich nicht gemacht.

Falls eine Seite mehrere Formulare bzw. andere Formulare enthält, sucht das Script speziell nach Formularen, die eine bestimmte CSS-Klasse zugeordnet haben. Das klappt so wie es jetzt geschrieben ist allerdings nur mit einem solchen Formular pro Seite. Man kann es aber sicher leicht umschreiben, so dass der anfängliche Formularzustand in einem Array statt einer einzelnen Variable gemerkt wird. Zudem sollte man im Blick behalten, dass das Script alle anfänglichen Inhalte des Formulars im Speicher des Browsers halten muss. Bei wirklich riesigen Formularen ist das vielleicht keine gute Idee. Für alle anderen bietet der Vergleich aber den Vorteil, dass der Nutzer auch in das Formular hinein klicken und z.B. eine Selectbox mehrmals ändern kann. Wenn am Ende wieder der Anfangszustand besteht, wird beim Verlassen nicht nachgefragt.

Nicht geprüft werden können übrigens Datei-Upload-Felder. Ändert man also sonst nichts im Formular und wählt nur eine Datei aus, dann kommt die Abfrage beim Verlassen der Seite nicht.

Ok, genug gelabert, hier ist der Code. Zuerst der Code, der beim Laden der Seite ausgeführt wird. Er hält den Formularzustand fest und fügt den Event-Handler für das Verlassen der Seite hinzu.

/**
 * Scripts die beim Laden der Seite laufen sollen.
 */
document.observe('dom:loaded', function() {
  // Formularzustand beim Laden der Seite merken
  abc_clean_form = abc_serialize_form();
  // beim Submit des Forms keinen Vergleich ausführen
  $$('form.abc_unsaved_changes').invoke('observe', 'submit', function() { abc_clean_form = null; } );
  // Formularzustand beim Verlassen vergleichen
  window.onbeforeunload = function (e) {
    var e = e || window.event;
    var res = abc_unsaved_changes();
    if (res) {
      if (e) {
        // IE + Firefox < 4
        e.returnValue = res;
      }
      return res;
    }
  };
});

Getestet mit: Prototype 1.7

Der Code ist etwas komplizierter um die Event-Modelle der verschiedenen Browser abzudecken. Weiter geht es mit der Funktion, die das Formular serialisiert. Das Formular muss die Klasse „abc_unsaved_changes“ haben.

/**
 * Serialisiert das gegebene Formular.
 */
function abc_serialize_form() {
  var abc_form = $$('form.abc_unsaved_changes');
  if (abc_form && abc_form.first()) {
    // Formular serialisieren
    var serialized_form = abc_form.first().serialize();
    // Wenn Validierungsfehler auftraten wurden eventuell Änderungen abgeschickt, die
    // von obigem JavaScript nicht erfasst werden. Dann immer time() ergänzen um
    // Meldung zu erzwingen.
    if ($$('form.abc_unsaved_changes .abc_error_header').length > 0) {
      serialized_form = new Date().getTime();
    }
    return serialized_form;
  }
  return null;
}

Neben dem allgemeinen Formularzustand prüft das Script auch auf das Vorhandensein eines divs mit einer bestimmten Klasse („abc_error_header“). Das ist das Element, in dem Validierungsfehler ausgegeben werden. Schickt man das Formular ab und die Seite wird mit Validierungsfehlern neu geladen, dann ist der Anfangszustand des Formulars ja nicht mehr aussagekräftig. Wir gehen dann einfach immer davon aus, dass Änderungen vorliegen.

Und nun der Code, der beim Verlassen der Seite ausgeführt wird.

/**
 * Prüft ob das Formular im Vergleich zum Laden der Seite geändert wurde und
 * gibt ggf. eine Warnmeldung aus.
 * 
 * @param e Das Event-Objekt.
 * 
 * @returns Die Warnmeldung, die der Browser als confirm anzeigen soll.
 */
function abc_unsaved_changes(e) {
  var abc_form = $$('form.abc_unsaved_changes');
  if (abc_form && abc_form.first() && abc_clean_form) {
    var form_dirty = abc_serialize_form();
    if (abc_clean_form != form_dirty) {
      return 'Es gibt ungespeicherte Änderungen im Formular. Wollen Sie die Seite wirklich verlassen?';
    }
  }
  return false;
}

Firefox zeigt ab Version 4 übrigens die zurückgegebene Meldung nicht mehr an, sondern gibt einen eigenen allgemeinen Text aus, der den Nutzer fragt, ob er die Seite wirklich verlassen möchte. Der IE 8 kombiniert die Nutzer-Meldung mit einem eigenen Text. Beides ist wohl dazu gedacht, den Missbrauch dieser Funktionalität einzudämmen, denn den Nutzer kann man an der Stelle natürlich alles Mögliche fragen.

Alles in allem bin ich ziemlich zufrieden, wie übersichtlich diese Lösung geworden ist und wie gut sie doch funktioniert. Vor allem kann man das Script so einsetzen, indem man es einfach in die entprechende JavaScript-Datei einfügt. Weitere Änderungen an der Seite sind nicht nötig, außer dass man eben die nötigen CSS-Klassen vergibt.

2 Gedanken zu „Warnung bei ungespeicherten Änderungen

  1. Mit garlic.js gibt es eine JS Lib die einene etwas anderen Ansatz verfolgt. Auf jQuery Basis werden die Änderungen mit localStorage zwischenspeichert. Erst mit Submit wird dies gelöscht so dass sogar beim Absturz des Browser nichts verloren geht! http://garlicjs.org/
    Könnte man gut kombinieren!

  2. Danke für den Hinweis! Für moderne Projekte ist das sicher ein spannender Ansatz. Man muss ja auch echt nicht immer das Rad neu erfinden. Manchmal lässt es sich leider nicht ändern, dass man mit älteren Libraries arbeiten oder gar alte IE-Versionen unterstützen muss…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Bitte beachte die Kommentarregeln: 1) Kein Spam, und bitte höflich bleiben. 2) Ins Namensfeld gehört ein Name. Gerne ein Pseudonym, aber bitte keine Keywords. 3) Keine kommerziellen Links, außer es hat Bezug zum Beitrag. mehr Details...

So, noch mal kurz drüber schauen und dann nichts wie ab damit. Vielen Dank fürs Kommentieren! :-)