Nicht gespeicherte Änderungen erkennen

94

Ich muss eine Eingabeaufforderung "Nicht gespeicherte Änderungen" in einer ASP .Net-Anwendung implementieren. Wenn ein Benutzer Steuerelemente in einem Webformular ändert und versucht, vor dem Speichern zu navigieren, sollte eine Eingabeaufforderung angezeigt werden, die ihn vor nicht gespeicherten Änderungen warnt, und ihm die Option geben, abzubrechen und auf der aktuellen Seite zu bleiben. Die Eingabeaufforderung sollte nicht angezeigt werden, wenn der Benutzer keines der Steuerelemente berührt hat.

Idealerweise würde ich dies gerne in JavaScript implementieren. Gibt es jedoch bereits vorhandene Frameworks oder empfohlene Entwurfsmuster, um dies zu erreichen, bevor ich meinen eigenen Code einrolle? Idealerweise möchte ich etwas, das mit minimalen Änderungen problemlos auf mehreren Seiten wiederverwendet werden kann.

tbreffni
quelle
Ich interessiere mich für das Gleiche, nur nicht für asp.net, aber ich denke, es gibt da draußen eine allgemeine Lösung.
Michael Neale
Die Lösung, die ich unten gepostet habe, kann in einfachem HTML / Javscript verwendet werden. Ändern Sie einfach das "Codebehind" in Attribute in den HTML-Tags selbst.
Wayne
Ich weiß, dass ich 5 Jahre zu spät hier bin, aber ich denke, ich kann die vorherigen Lösungen verbessern ... Ich habe gerade meine Antwort unten korrigiert und aktualisiert.
Skibulk

Antworten:

96

Verwenden von jQuery:

var _isDirty = false;
$("input[type='text']").change(function(){
  _isDirty = true;
});
// replicate for other input types and selects

Nach Bedarf mit onunload/ onbeforeunloadMethoden kombinieren.

In den Kommentaren wird auf alle Eingabefelder verwiesen, ohne dass Code dupliziert wird:

$(':input').change(function () {

Verwenden $(":input")bezieht sich auf alle Eingabe-, Textbereichs-, Auswahl- und Schaltflächenelemente.

Aaron Powell
quelle
32
Upvote, weil ich diese Lösung verwendet habe, aber es gibt einen etwas einfacheren Weg. Wenn Sie verwenden: $ (': input'). Change (function () {dann funktioniert das für alle Eingabefelder, Sie müssen nicht für andere Eingabetypen replizieren. $ (": Input") wählt alle Eingaben aus, Textbereich , Elemente auswählen und Schaltflächen.
CodeClimber
5
Ich denke, dies kann Probleme in Firefox haben. Wenn ein Benutzer das Formular ändert und dann auf die Schaltfläche Aktualisieren des Browsers klickt, wird die Seite neu geladen und Firefox füllt das Formular mit den vorherigen Einträgen des Benutzers - ohne das Änderungsereignis auszulösen. Jetzt ist das Formular verschmutzt, aber das Flag wird erst gesetzt, wenn der Benutzer eine weitere Bearbeitung vornimmt.
Oskar Berggren
8
Nur ein Hinweis. Wenn das Formular verschmutzt ist, bedeutet dies nicht, dass es wirklich geänderte Daten enthält. Für eine bessere Benutzerfreundlichkeit müssen Sie alte Daten tatsächlich mit den neuen vergleichen;)
Slava Fomin II
Beachten Sie, dass changebei Texteingaben beim Verlassen / Verlieren des Fokus einmal ausgelöst wird, während inputbei jedem Tastendruck ausgelöst wird. (Ich benutze es normalerweise zusammen changemit JQuery's one(), um mehrere Ereignisse zu verhindern.)
SpazzMarticus
46

Ein Teil des Puzzles:

/**
 * Determines if a form is dirty by comparing the current value of each element
 * with its default value.
 *
 * @param {Form} form the form to be checked.
 * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code>
 *                   otherwise.
 */
function formIsDirty(form) {
  for (var i = 0; i < form.elements.length; i++) {
    var element = form.elements[i];
    var type = element.type;
    if (type == "checkbox" || type == "radio") {
      if (element.checked != element.defaultChecked) {
        return true;
      }
    }
    else if (type == "hidden" || type == "password" ||
             type == "text" || type == "textarea") {
      if (element.value != element.defaultValue) {
        return true;
      }
    }
    else if (type == "select-one" || type == "select-multiple") {
      for (var j = 0; j < element.options.length; j++) {
        if (element.options[j].selected !=
            element.options[j].defaultSelected) {
          return true;
        }
      }
    }
  }
  return false;
}

Und noch eine :

window.onbeforeunload = function(e) {
  e = e || window.event;  
  if (formIsDirty(document.forms["someForm"])) {
    // For IE and Firefox
    if (e) {
      e.returnValue = "You have unsaved changes.";
    }
    // For Safari
    return "You have unsaved changes.";
  }
};

Packen Sie alles zusammen und was bekommen Sie?

var confirmExitIfModified = (function() {
  function formIsDirty(form) {
    // ...as above
  }

  return function(form, message) {
    window.onbeforeunload = function(e) {
      e = e || window.event;
      if (formIsDirty(document.forms[form])) {
        // For IE and Firefox
        if (e) {
          e.returnValue = message;
        }
        // For Safari
        return message;
      }
    };
  };
})();

confirmExitIfModified("someForm", "You have unsaved changes.");

Möglicherweise möchten Sie auch die Registrierung des beforeunloadEreignishandlers ändern, um die Ereignisregistrierung zu verwenden LIBRARY_OF_CHOICE.

Jonny Buchanan
quelle
In Firefox 9.0.1 erhalte ich keine Nachricht. Ich habe eine Standardnachricht mit der Aufschrift "Sind Sie sicher, dass Sie die Seite verlassen möchten? Einige Daten gehen möglicherweise verloren" (Dies ist mehr oder weniger die Übersetzung der Nachricht, die mir mein nicht englischer Browser anzeigt)
Romain Guidoux
1
Dieses Skript ist fantastisch, aber für mich wurde beim Senden meines Formulars auch die Warnmeldung angezeigt. Ich habe dies behoben, indem ich das Ereignis in einem onClick ( onclick="window.onbeforeunload=null")
Joost
1
Ich mag den Ansatz, die Eigenschaften von defaultValue usw. zu vergleichen, anstatt ein schmutziges Bit zu setzen. Dies bedeutet, dass wenn jemand ein Feld ändert und es dann wieder ändert, das Formular nicht als fehlerhaft gemeldet wird.
Thelem
Danke, das ist großartig. Können Sie uns bitte sagen, wie wir uns für jquery registrieren sollen? Wenn ich dieses Skript nicht auf derselben HTML-Seite habe, wird es nicht ausgelöst. Wenn ich es in einer externen .js-Datei habe, funktioniert es nicht.
Shiva Naru
17

Auf der ASPX-Seite benötigen Sie eine Javascript-Funktion, um festzustellen, ob die Formularinformationen "schmutzig" sind oder nicht.

<script language="javascript">
    var isDirty = false;

    function setDirty() {
        isDirty = true;
    }

    function checkSave() {
        var sSave;
        if (isDirty == true) {
            sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving.");
            if (sSave == true) {
                document.getElementById('__EVENTTARGET').value = 'btnSubmit';
                document.getElementById('__EVENTARGUMENT').value = 'Click';  
                window.document.formName.submit();
            } else {
                 return true;
            }
        }
    }
</script>
<body class="StandardBody" onunload="checkSave()">

Fügen Sie im Codebehind die Trigger zu den Eingabefeldern hinzu und setzen Sie sie auf die Schaltflächen zum Senden / Abbrechen zurück.

btnSubmit.Attributes.Add("onclick", "isDirty = 0;");
btnCancel.Attributes.Add("onclick", "isDirty = 0;");
txtName.Attributes.Add("onchange", "setDirty();");
txtAddress.Attributes.Add("onchange", "setDirty();");
//etc..
Wayne
quelle
9

Im Folgenden werden die Onbeforeunload-Funktion und die JQuery des Browsers verwendet, um alle Onchange-Ereignisse zu erfassen. Die IT-Abteilung sucht auch nach Schaltflächen zum Senden oder Zurücksetzen, um das Flag zurückzusetzen, das anzeigt, dass Änderungen aufgetreten sind.

dataChanged = 0;     // global variable flags unsaved changes      

function bindForChange(){    
     $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1})
     $(':reset,:submit').bind('click',function(event) { dataChanged = 0 })
}


function askConfirm(){  
    if (dataChanged){ 
        return "You have some unsaved changes.  Press OK to continue without saving." 
    }
}

window.onbeforeunload = askConfirm;
window.onload = bindForChange;
Colin Houghton
quelle
Hallo Colin Reset hat nicht funktioniert, als ich ein Kontrollkästchen gesetzt habe, anfangs habe ich es aktiviert und wieder deaktiviert, aber es wird eine Warnung ausgelöst. Wie kann ich zurücksetzen, wenn der Benutzer das Kontrollkästchen deaktiviert hat
Entwickler
8

Vielen Dank für die Antworten an alle. Am Ende habe ich eine Lösung mit JQuery und dem Protect-Data- Plug-In implementiert . Auf diese Weise kann ich die Überwachung automatisch auf alle Steuerelemente auf einer Seite anwenden.

Es gibt jedoch einige Einschränkungen, insbesondere beim Umgang mit einer ASP .Net-Anwendung:

  • Wenn ein Benutzer die Option Abbrechen wählt, gibt die Funktion doPostBack einen JavaScript-Fehler aus. Ich musste den .submit-Aufruf in doPostBack manuell mit einem Try-Catch versehen, um ihn zu unterdrücken.

  • Auf einigen Seiten kann ein Benutzer eine Aktion ausführen, die ein Postback auf derselben Seite ausführt, jedoch kein Speichern ist. Dies führt dazu, dass die JavaScript-Logik zurückgesetzt wird, sodass sich nach dem Postback nichts geändert hat, wenn sich etwas geändert hat. Ich musste ein verstecktes Textfeld implementieren, das mit der Seite zurückgesendet wird und einen einfachen booleschen Wert enthält, der angibt, ob die Daten verschmutzt sind. Dies wird über Postbacks hinweg beibehalten.

  • Möglicherweise möchten Sie, dass einige Postbacks auf der Seite den Dialog nicht auslösen, z. B. eine Schaltfläche Speichern. In diesem Fall können Sie mit JQuery eine OnClick-Funktion hinzufügen, mit der window.onbeforeunload auf null gesetzt wird.

Hoffentlich ist dies hilfreich für alle anderen, die etwas Ähnliches implementieren müssen.

tbreffni
quelle
7

Allgemeine Lösung Unterstützung mehrerer Formulare auf einer bestimmten Seite ( einfach kopieren und in Ihr Projekt einfügen )

$(document).ready(function() {
    $('form :input').change(function() {
        $(this).closest('form').addClass('form-dirty');
    });

    $(window).bind('beforeunload', function() {
        if($('form:not(.ignore-changes).form-dirty').length > 0) {
            return 'You have unsaved changes, are you sure you want to discard them?';
        }
    });

    $('form').bind('submit',function() {
        $(this).closest('form').removeClass('form-dirty');
        return true;
    });
});

Hinweis: Diese Lösung wird aus den Lösungen anderer kombiniert, um eine allgemeine integrierte Lösung zu erstellen.

Eigenschaften:

  • Einfach kopieren und in Ihre App einfügen.
  • Unterstützt mehrere Formulare.
  • Sie können Formulare schmutzig gestalten oder Aktionen verschmutzen, da sie die Klasse "formverschmutzt" haben.
  • Sie können einige Formulare ausschließen, indem Sie die Klasse 'Änderungen ignorieren' hinzufügen.
MhdSyrwan
quelle
Das funktioniert gut, aber ich ersetze die hinzugefügten Klassen durch die _isDirtyvon Aaron Powell beschriebene Variable. Ich habe nicht die Notwendigkeit gesehen, Klassen anstelle von Variablen im Javascript zum HTML hinzuzufügen und daraus zu entfernen.
Martin
1
Die Idee, hier HTML-Klassen zu verwenden, besteht darin, jedem Formular das Attribut 'schmutzig' beizufügen, da Sie nicht einfach Variablen verwenden können, um eine allgemeine js / html-Lösung mit Anwendungsgröße zu erstellen.
MhdSyrwan
1
Mit anderen Worten, die Verwendung einer Variablen funktioniert nur für ein einzelnes Formular. Andernfalls benötigen Sie eine Reihe von Dirty Flags, wodurch die Lösung erheblich komplizierter wird.
MhdSyrwan
1
Vielen Dank, dass Sie diesen Punkt erklärt haben. Mein Bedarf war nur mit einer einzigen Formularseite.
Martin
6

Die folgende Lösung funktioniert für Prototypen (getestet in FF, IE 6 und Safari). Es wird ein generischer Formularbeobachter verwendet (der das Formular auslöst: geändert, wenn Felder des Formulars geändert wurden), den Sie auch für andere Elemente verwenden können.

/* use this function to announce changes from your own scripts/event handlers.
 * Example: onClick="makeDirty($(this).up('form'));"
 */
function makeDirty(form) {
    form.fire("form:changed");
}

function handleChange(form, event) {
    makeDirty(form);
}

/* generic form observer, ensure that form:changed is being fired whenever
 * a field is being changed in that particular for
 */
function setupFormChangeObserver(form) {
    var handler = handleChange.curry(form);

    form.getElements().each(function (element) {
        element.observe("change", handler);
    });
}

/* installs a form protector to a form marked with class 'protectForm' */
function setupProtectForm() {
    var form = $$("form.protectForm").first();

    /* abort if no form */
    if (!form) return;

    setupFormChangeObserver(form);

    var dirty = false;
    form.observe("form:changed", function(event) {
        dirty = true;
    });

    /* submitting the form makes the form clean again */
    form.observe("submit", function(event) {
        dirty = false;
    });

    /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */
    window.onbeforeunload = function(event) {
        if (dirty) {
            return "There are unsaved changes, they will be lost if you leave now.";
        }
    };
}

document.observe("dom:loaded", setupProtectForm);
reto
quelle
6

Hier ist eine einfache Javascript / JQuery-Lösung. Es berücksichtigt "Rückgängigmachungen" durch den Benutzer, ist zur Vereinfachung der Anwendung in einer Funktion gekapselt und löst beim Senden keine Fehlzündungen aus. Rufen Sie einfach die Funktion auf und übergeben Sie die ID Ihres Formulars.

Diese Funktion serialisiert das Formular einmal beim Laden der Seite und erneut, bevor der Benutzer die Seite verlässt. Wenn die beiden Formularzustände unterschiedlich sind, wird die Eingabeaufforderung angezeigt.

Probieren Sie es aus: http://jsfiddle.net/skibulk/Ydt7Y/

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.";
    };
}

$(function(){
    formUnloadPrompt('form');
});
Skibulk
quelle
2
Ich habe deinen Ansatz ausprobiert. Es funktioniert für Chrome und Firefox, kann aber nicht für Safari auf dem Ipad Mini verwendet werden.
Dimitry K
4

Ich habe kürzlich zu einem Open-Source-jQuery-Plugin namens dirtyForms beigetragen .

Das Plugin ist für die Arbeit mit dynamisch hinzugefügtem HTML ausgelegt, unterstützt mehrere Formulare, unterstützt praktisch jedes Dialog-Framework, greift vor dem Entladen des Dialogfelds auf den Browser zurück und verfügt über ein steckbares Hilfs-Framework, um das Abrufen des Status von benutzerdefinierten Editoren zu unterstützen (ein tinyMCE-Plugin ist enthalten). , funktioniert in iFrames und der Dirty-Status kann nach Belieben eingestellt oder zurückgesetzt werden.

https://github.com/snikch/jquery.dirtyforms

NightOwl888
quelle
4

Das Erkennen von Formularänderungen mithilfe von jQuery ist sehr einfach:

var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed

// check for form changes
if ($('#formId').serialize() != formInitVal) {
    // show confirmation alert
}
v.babak
quelle
2

Ich habe den obigen Vorschlag von Slace erweitert, um die meisten bearbeitbaren Elemente einzuschließen und auch bestimmte Elemente (mit einem CSS-Stil namens "srSearch" hier) vom Setzen des Dirty-Flags auszuschließen.

<script type="text/javascript">
        var _isDirty = false;
        $(document).ready(function () {            

            // Set exclude CSS class on radio-button list elements
            $('table.srSearch input:radio').addClass("srSearch");

            $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () {
                _isDirty = true;
            });
        });

        $(window).bind('beforeunload', function () {
            if (_isDirty) {
                return 'You have unsaved changes.';
            }
        });        

PeterX
quelle
2
      var unsaved = false;
    $(":input").change(function () {         
        unsaved = true;
    });

    function unloadPage() {         
        if (unsaved) {             
          alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
        }
    } 

window.onbeforeunload = unloadPage;

Ankit
quelle
0

Eine Methode , bei der Arrays zum Speichern der Variablen verwendet werden, damit Änderungen nachverfolgt werden können.

Hier ist eine sehr einfache Methode, um Änderungen zu erkennen , aber der Rest ist nicht so elegant.

Eine andere Methode, die ziemlich einfach und klein ist, aus Farfetched Blog :

<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()">
<form>
<select name=a multiple>
 <option value=1>1
 <option value=2>2
 <option value=3>3
</select>
<input name=b value=123>
<input type=submit>
</form>

<script>
var changed = 0;
function recordChange() {
 changed = 1;
}
function recordChangeIfChangeKey(myevent) {
 if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey)
  recordChange(myevent);
}
function ignoreChange() {
 changed = 0;
}
function lookForChanges() {
 var origfunc;
 for (i = 0; i < document.forms.length; i++) {
  for (j = 0; j < document.forms[i].elements.length; j++) {
   var formField=document.forms[i].elements[j];
   var formFieldType=formField.type.toLowerCase();
   if (formFieldType == 'checkbox' || formFieldType == 'radio') {
    addHandler(formField, 'click', recordChange);
   } else if (formFieldType == 'text' || formFieldType == 'textarea') {
    if (formField.attachEvent) {
     addHandler(formField, 'keypress', recordChange);
    } else {
     addHandler(formField, 'keypress', recordChangeIfChangeKey);
    }
   } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') {
    addHandler(formField, 'change', recordChange);
   }
  }
  addHandler(document.forms[i], 'submit', ignoreChange);
 }
}
function warnOfUnsavedChanges() {
 if (changed) {
  if ("event" in window) //ie
   event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.';
  else //netscape
   return false;
 }
}
function addHandler(target, eventName, handler) {
 if (target.attachEvent) {
  target.attachEvent('on'+eventName, handler);
 } else {
  target.addEventListener(eventName, handler, false);
 }
}
</script>
Adam Davis
quelle
0

In IE document.ready funktioniert nicht richtig, es werden die Werte der Eingabe aktualisiert.

Daher müssen wir das Ladeereignis innerhalb der document.ready-Funktion binden, die auch für den IE-Browser geeignet ist.

Unten finden Sie den Code, den Sie in die Funktion document.ready einfügen sollten.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Krupall
quelle