Rilevamento delle modifiche non salvate


94

Ho un requisito per implementare un prompt "Modifiche non salvate" in un'applicazione ASP .Net. Se un utente modifica i controlli in un modulo Web e tenta di uscire prima di salvare, dovrebbe apparire un messaggio che lo avverte che hanno modifiche non salvate e dare loro la possibilità di annullare e rimanere sulla pagina corrente. Il prompt non dovrebbe essere visualizzato se l'utente non ha toccato nessuno dei controlli.

Idealmente mi piacerebbe implementarlo in JavaScript, ma prima di intraprendere il percorso di rollare il mio codice, ci sono framework esistenti o schemi di progettazione consigliati per raggiungere questo obiettivo? Idealmente, vorrei qualcosa che possa essere facilmente riutilizzato su più pagine con modifiche minime.


Mi interessa lo stesso, ma non per asp.net, ma penso che ci sia una soluzione generale là fuori.
Michael Neale

La soluzione che ho pubblicato di seguito può essere utilizzata in semplice HTML / Javscript. Cambia semplicemente il "codebehind" in attributi nei tag HTML stessi.
Wayne

So di essere in ritardo di 5 anni qui, ma penso di poter migliorare le soluzioni precedenti ... Ho appena corretto e aggiornato la mia risposta di seguito.
skibulk

Risposte:


96

Utilizzando jQuery:

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

Combina con onunload/ onbeforeunloadmetodi come richiesto.

Dai commenti, quanto segue fa riferimento a tutti i campi di input, senza duplicare il codice:

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

L'utilizzo si $(":input")riferisce a tutti gli elementi input, textarea, select e button.


32
Voto positivo perché ho usato questa soluzione ma c'è un modo leggermente più semplice. Se usi: $ (': input'). Change (function () {allora funziona per tutti i campi di input, non è necessario replicare per altri tipi di input. $ (": Input") seleziona tutti gli input, textarea , selezionare e pulsanti elementi
CodeClimber

5
Penso che questo possa avere problemi in Firefox. Se un utente modifica il modulo, quindi fa clic sul pulsante Aggiorna del browser, la pagina verrà ricaricata e Firefox popolerà il modulo con le voci precedenti dell'utente, senza attivare l'evento di modifica. Ora il modulo sarà sporco, ma il flag non verrà impostato a meno che l'utente non effettui un'altra modifica.
Oskar Berggren

8
Solo un suggerimento. Se il modulo è sporco, non significa ancora che contenga dati realmente modificati. Per una migliore usabilità devi effettivamente confrontare i vecchi dati con quelli nuovi;)
Slava Fomin II

Tieni presente che changeper gli input di testo si attiva una volta quando si lascia / perde il focus, mentre si inputattiva per ogni pressione di tasto. (Di solito lo uso insieme changea JQuery one(), per evitare più eventi.)
SpazzMarticus

46

Un pezzo del puzzle:

/**
 * 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;
}

E un altro :

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

Concludi tutto e cosa ottieni?

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

Probabilmente vorrai anche cambiare la registrazione del beforeunloadgestore dell'evento per usare LIBRARY_OF_CHOICEla registrazione dell'evento di.


In Firefox 9.0.1 non ricevo alcun messaggio. Ho un messaggio predefinito che dice "Sei sicuro di voler lasciare la pagina? Alcuni dati potrebbero essere persi" (Questa è più o meno la traduzione del messaggio che mi viene visualizzato dal mio browser non inglese)
Romain Guidoux

1
Questo script è fantastico, ma per me mostrava anche il messaggio di avviso al momento dell'invio del modulo. Ho risolto il problema annullando il binding dell'evento in un onClick ( onclick="window.onbeforeunload=null")
Joost

1
Mi piace l'approccio di confrontare le proprietà defaultValue ecc piuttosto che impostare un po 'sporco. Ciò significa che se qualcuno modifica un campo, quindi lo modifica di nuovo, il modulo non verrà segnalato come sporco.
thelem

Grazie, è fantastico. Puoi dirci come registrarci a jquery? A meno che non abbia questo script sulla stessa pagina html, non si attiva. Se l'ho all'interno di un file .js esterno, non funziona.
Shiva Naru

17

Nella pagina .aspx, hai bisogno di una funzione Javascript per dire se le informazioni del modulo sono "sporche" o meno

<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()">

e nel codebehind, aggiungi i trigger ai campi di input e ripristina i pulsanti di invio / cancellazione ....

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

9

Quanto segue utilizza la funzione onbeforeunload del browser e jquery per acquisire qualsiasi evento onchange. Inoltre, cerca eventuali pulsanti di invio o ripristino per reimpostare il flag che indica che sono state apportate modifiche.

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;

Ciao colin reset non ha funzionato quando ho inserito una casella di controllo, inizialmente l'ho selezionata e deselezionata di nuovo ma l'avviso viene attivato, come posso ripristinare quando l'utente ha deselezionato la casella di controllo
Sviluppatore

8

Grazie per le risposte a tutti. Ho finito per implementare una soluzione utilizzando JQuery e il plug-in Protect-Data . Ciò mi consente di applicare automaticamente il monitoraggio a tutti i controlli su una pagina.

Tuttavia, ci sono alcuni avvertimenti, soprattutto quando si ha a che fare con un'applicazione ASP .Net:

  • Quando un utente sceglie l'opzione di annullamento, la funzione doPostBack genererà un errore JavaScript. Ho dovuto inserire manualmente un try-catch attorno alla chiamata .submit all'interno di doPostBack per sopprimerlo.

  • In alcune pagine, un utente può eseguire un'azione che esegue un postback sulla stessa pagina, ma non è un salvataggio. Ciò si traduce in qualsiasi reimpostazione della logica JavaScript, quindi pensa che nulla sia cambiato dopo il postback quando qualcosa potrebbe essere cambiato. Ho dovuto implementare una casella di testo nascosta che viene postata di nuovo con la pagina e viene utilizzata per contenere un semplice valore booleano che indica se i dati sono sporchi. Questo viene mantenuto in tutti i postback.

  • Potresti volere che alcuni postback sulla pagina non attivino la finestra di dialogo, come un pulsante Salva. In questo caso, puoi utilizzare JQuery per aggiungere una funzione OnClick che imposta window.onbeforeunload su null.

Si spera che questo sia utile per chiunque altro debba implementare qualcosa di simile.


7

Soluzione generale Supporto di più moduli in una determinata pagina ( basta copiare e incollare nel progetto )

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

Nota: questa soluzione viene combinata con soluzioni di altri per creare una soluzione integrata generale.

Caratteristiche:

  • Basta copiare e incollare nella tua app.
  • Supporta più moduli.
  • Puoi modellare o eseguire azioni in forme sporche, poiché hanno la classe "form-dirty".
  • Puoi escludere alcuni moduli aggiungendo la classe "ignora-modifiche".

Funziona bene, ma sostituisco le classi aggiunte con la _isDirtyvariabile come descritto da Aaron Powell. Non ho visto la necessità di aggiungere e rimuovere classi dall'HTML invece di variabili nel javascript.
Martin

1
L'idea di utilizzare le classi html qui è di allegare l'attributo "sporco" a ciascun modulo poiché non è possibile utilizzare semplicemente le variabili per creare una soluzione generale application-wize js / html.
MhdSyrwan

1
In altre parole, l'uso di una variabile funzionerà solo per una singola forma, altrimenti avrai bisogno di un array di flag sporchi che renderanno la soluzione molto più complicata.
MhdSyrwan

1
Grazie per aver spiegato questo punto. La mia esigenza era solo con una singola pagina del modulo.
Martin,

6

La seguente soluzione funziona per il prototipo (testato in FF, IE 6 e Safari). Utilizza un osservatore generico del modulo (che attiva il modulo: cambiato quando qualsiasi campo del modulo è stato modificato), che puoi usare anche per altre cose.

/* 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);

6

Ecco una soluzione javascript / jquery semplice. Tiene conto degli "annullamenti" da parte dell'utente, è incapsulato in una funzione per facilitarne l'applicazione e non si attiva male al momento dell'invio. Basta chiamare la funzione e passare l'ID del modulo.

Questa funzione serializza il modulo una volta quando la pagina viene caricata e di nuovo prima che l'utente lasci la pagina. Se i due stati del modulo sono diversi, viene visualizzato il prompt.

Provalo: 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');
});

2
Ho provato il tuo approccio. Funziona per Chrome e Firefox, ma non può farlo funzionare per Safari su Ipad Mini ..
Dimitry K

4

Recentemente ho contribuito a un plugin jQuery open source chiamato dirtyForms .

Il plug-in è progettato per funzionare con HTML aggiunto dinamicamente, supporta più moduli, può supportare praticamente qualsiasi framework di dialogo, ricorre al browser prima di scaricare la finestra di dialogo, ha un framework di supporto collegabile per supportare lo stato di sporco da editor personalizzati (è incluso un plugin tinyMCE) , funziona all'interno di iFrame e lo stato sporco può essere impostato o ripristinato a piacimento.

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


4

Rilevare le modifiche al modulo utilizzando jQuery è molto semplice:

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

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

2

Ho ampliato il suggerimento di Slace sopra, per includere la maggior parte degli elementi modificabili e anche escludere alcuni elementi (con uno stile CSS chiamato "srSearch" qui) dal causare l'impostazione del flag sporco.

<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.';
            }
        });        


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;



0

Un metodo , che utilizza gli array per contenere le variabili in modo da poter tenere traccia delle modifiche.

Ecco un metodo molto semplice per rilevare le modifiche , ma il resto non è così elegante.

Un altro metodo abbastanza semplice e piccolo, dal blog Farfetched :

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

0

In IE document.ready non funzionerà correttamente aggiornerà i valori di input.

quindi dobbiamo associare l'evento di caricamento all'interno della funzione document.ready che gestirà anche per il browser IE.

di seguito è riportato il codice da inserire nella funzione document.ready.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.