Avvisa l’utente prima di lasciare la pagina Web con le modifiche non salvate

Ho alcune pagine con moduli nella mia applicazione.

Come posso proteggere il modulo in modo che se qualcuno naviga via o chiude la scheda del browser, dovrebbe essere invitato a confermare che vuole davvero lasciare il modulo con i dati non salvati?

Breve risposta sbagliata:

È ansible farlo gestendo l’evento beforeunload e restituendo una stringa non null :

 window.addEventListener("beforeunload", function (e) { var confirmationMessage = 'It looks like you have been editing something. ' + 'If you leave before saving, your changes will be lost.'; (e || window.event).returnValue = confirmationMessage; //Gecko + IE return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc. }); 

Il problema con questo approccio è che l’ invio di un modulo sta anche facendo scattare l’evento di scarico . Questo problema viene risolto facilmente aggiungendo il flag a cui stai inviando un modulo:

 var formSubmitting = false; var setFormSubmitting = function() { formSubmitting = true; }; window.onload = function() { window.addEventListener("beforeunload", function (e) { if (formSubmitting) { return undefined; } var confirmationMessage = 'It looks like you have been editing something. ' + 'If you leave before saving, your changes will be lost.'; (e || window.event).returnValue = confirmationMessage; //Gecko + IE return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc. }); }; 

Quindi chiama il setter quando invii:

 

Ma continua a leggere …

Risposta lunga e corretta:

Inoltre, non vuoi mostrare questo messaggio quando l’utente non ha cambiato nulla sui tuoi moduli . Una soluzione è utilizzare l’evento beforeunload in combinazione con un flag “dirty”, che triggers il prompt solo se è veramente rilevante.

 var isDirty = function() { return false; } window.onload = function() { window.addEventListener("beforeunload", function (e) { if (formSubmitting || !isDirty()) { return undefined; } var confirmationMessage = 'It looks like you have been editing something. ' + 'If you leave before saving, your changes will be lost.'; (e || window.event).returnValue = confirmationMessage; //Gecko + IE return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc. }); }; 

Ora per implementare il metodo isDirty , ci sono vari approcci.

È ansible utilizzare jQuery e serializzazione dei moduli , ma questo approccio presenta alcuni difetti. Per prima cosa devi modificare il codice per lavorare su qualsiasi modulo ( $("form").each() farà), ma il problema più grande è che serialize() di jQuery funzionerà solo su elementi con nome, non disabilitati, quindi cambiando qualsiasi elemento disabilitato o senza nome non attiverà la bandiera sporca. Esistono soluzioni alternative , come la creazione di controlli di sola lettura invece di abilitare, serializzare e quindi disabilitare nuovamente i controlli.

Quindi gli eventi sembrano la strada da percorrere. Puoi provare ad ascoltare i tasti premuti . Questo evento ha alcuni problemi:

  • Non si triggers su caselle di controllo, pulsanti di opzione o altri elementi che vengono modificati tramite l’input del mouse.
  • Si innescherà per pressioni di tasti irrilevanti come il tasto Ctrl .
  • Non si triggers sui valori impostati tramite il codice JavaScript.
  • Non si innesca quando si taglia o incolla il testo attraverso i menu di scelta rapida.
  • Non funzionerà con input virtuali come datepicker o abbellitori checkbox / radiobutton che ne salvano il valore in un input nascosto tramite JavaScript.

L’ evento change non si triggers anche sui valori impostati dal codice JavaScript , quindi non funzionerà anche per gli input virtuali.

Associare l’evento di input a tutti gli input s (e alle textarea e alla select s) sulla tua pagina non funzionerà sui browser più vecchi e, come tutte le soluzioni di gestione degli eventi sopra menzionate, non supporta l’annullamento. Quando un utente cambia una casella di testo e poi annulla la posta, o controlla e deseleziona una casella di controllo, il modulo è ancora considerato sporco.

E quando vuoi attuare più comportamenti, come ignorare determinati elementi, avrai ancora più lavoro da fare.

Non reinventare la ruota:

Quindi, prima di pensare all’implementazione di queste soluzioni e di tutte le soluzioni alternative necessarie, comprendi che stai reinventando la ruota e che sei incline a incorrere in problemi che altri hanno già risolto per te.

Se la tua applicazione utilizza già jQuery, puoi utilizzare un codice testato e mantenuto invece di eseguire il rollover e utilizzare una libreria di terze parti per tutto questo. jQuery sei sicuro? plugin funziona alla grande, vedi la loro pagina demo . È così semplice:

   

I messaggi personalizzati non sono supportati ovunque

Tieni presente che Firefox 4 non ha supportato i messaggi personalizzati in questa finestra di dialogo. A partire dal mese scorso, Chrome 51 è in fase di implementazione in cui vengono rimossi anche i messaggi personalizzati .

Alcune alternative esistono altrove su questo sito, ma penso che un dialogo come questo sia abbastanza chiaro:

Vuoi lasciare questo sito?

Le modifiche apportate potrebbero non essere salvate.

Lascia stare

Controlla l’ evento onbeforeunload di JavaScript. È un JavaScript non standard introdotto da Microsoft, tuttavia funziona nella maggior parte dei browser e la loro documentazione onbeforeunload ha più informazioni ed esempi.

via jquery

 $('#form').data('serialize',$('#form').serialize()); // On load save form current state $(window).bind('beforeunload', function(e){ if($('#form').serialize()!=$('#form').data('serialize'))return true; else e=null; // ie; if form state change show warning box, else don't show it. }); 

Puoi utilizzare la funzione Serializzazione modulo JQuery di Google, che raccoglierà tutti gli input dei moduli e li salverà nell’array. Immagino che questo spieghi sia abbastanza 🙂

In base alle risposte precedenti e raggruppate in vari punti nello stack overflow, ecco la soluzione che ho elaborato per gestire il caso quando in realtà si desidera inviare le modifiche:

 window.thisPage = window.thisPage || {}; window.thisPage.isDirty = false; window.thisPage.closeEditorWarning = function (event) { if (window.thisPage.isDirty) return 'It looks like you have been editing something' + ' - if you leave before saving, then your changes will be lost.' else return undefined; }; $("form").on('keyup', 'textarea', // You can use input[type=text] here as well. function () { window.thisPage.isDirty = true; }); $("form").submit(function () { QC.thisPage.isDirty = false; }); window.onbeforeunload = window.thisPage.closeEditorWarning; 

Vale la pena notare che IE11 sembra richiedere che la funzione closeEditorWarning ritorni undefined per non mostrare un avviso.

Il seguente codice funziona alla grande. Devi raggiungere i cambiamenti di input dei tuoi elementi del modulo tramite l’attributo id:

 var somethingChanged=false; $('#managerForm input').change(function() { somethingChanged = true; }); $(window).bind('beforeunload', function(e){ if(somethingChanged) return "You made some changes and it's not saved?"; else e=null; // ie; if form state change show warning box, else don't show it. }); }); 

Il seguente one-liner ha funzionato per me.

 window.onbeforeunload = s => modified ? "" : null; 

Basta impostare modified su true o false a seconda dello stato dell’applicazione.

 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; 

Risposta breve:

 let pageModified = true window.addEventListener("beforeunload", () => pageModified ? 'Close page without saving data?' : null ) 

Aggiungendo l’idea di @codecaster è ansible aggiungere questo ad ogni pagina con un modulo (nel mio caso lo uso in modo globale in modo che solo sui moduli avrebbe questo avviso) cambiare la sua funzione in

 if ( formSubmitting || document.getElementsByTagName('form').length == 0) 

Inserisci anche i moduli di invio inclusi i pulsanti di accesso e in annullamento dei pulsanti in modo che quando la persona premi annulla o invii il modulo non attiverà l’avviso anche in ogni pagina senza un modulo …

 Cancel 

È ansible controllare una spiegazione dettagliata qui: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/ confronto-di-form-values-on-load-e -prima-vicino

Il codice principale:

 function formCompare(defaultValues, valuesOnClose) { // Create arrays of property names var aPropsFormLoad = Object.keys(defaultValues); var aPropsFormClose = Object.keys(valuesOnClose); // If number of properties is different, // objects are not equivalent if (aPropsFormLoad.length != aPropsFormClose.length) { return false; } for (var i = 0; i < aPropsFormLoad.length; i++) { var propName = aPropsFormLoad[i]; // If values of same property are not equal, // objects are not equivalent if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") { return false; } } // If we made it this far, objects // are considered equivalent return true; } //add polyfill for older browsers, as explained on the link above //use the block below on load for(i=0; i < document.forms[0].elements.length; i++){ console.log("The field name is: " + document.forms[0].elements[i].name + " and it's value is: " + document.forms[0].elements[i].value ); aPropsFormLoad[i] = document.forms[0].elements[i].value; } //create a similar array on window unload event. //and call the utility function if (!formCompare(aPropsOnLoad, aPropsOnClose)) { //perform action: //ask user for confirmation or //display message about changes made } 

Soluzione universale che non richiede alcuna configurazione che rilevi automaticamente tutte le modifiche di input, compresi gli elementi contenteditable:

 "use strict"; (() => { const modified_inputs = new Set; const defaultValue = "defaultValue"; // store default values addEventListener("beforeinput", (evt) => { const target = evt.target; if (!(defaultValue in target || defaultValue in target.dataset)) { target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim(); } }); // detect input modifications addEventListener("input", (evt) => { const target = evt.target; let original; if (defaultValue in target) { original = target[defaultValue]; } else { original = target.dataset[defaultValue]; } if (original !== ("" + (target.value || target.textContent)).trim()) { if (!modified_inputs.has(target)) { modified_inputs.add(target); } } else if (modified_inputs.has(target)) { modified_inputs.delete(target); } }); addEventListener("beforeunload", (evt) => { if (modified_inputs.size) { const unsaved_changes_warning = "Changes you made may not be saved."; evt.returnValue = unsaved_changes_warning; return unsaved_changes_warning; } }); })(); 

Prima di tutto, la maggior parte dei browser ha questa funzione di default. E perché hai bisogno di questo? Perché non mantenere il modulo sincronizzato? Voglio dire, salvalo su qualsiasi cambiamento senza attendere l’invio da parte dell’utente. Come fanno i contatti di Google. Ovviamente se tutti i campi nel modulo sono obbligatori. Gli utenti non amano quando costringono a riempire qualcosa senza la possibilità di andare via a pensare se ne hanno bisogno. 🙂