Avertir l'utilisateur avant de quitter la page Web avec des modifications non sauvegardées

j'ai des pages avec des formulaires dans ma demande.

Comment puis-je sécuriser le formulaire de telle manière que si quelqu'un navigue ou ferme l'onglet navigateur, il devrait être invité à confirmer qu'il veut vraiment quitter le formulaire avec des données non sauvegardées?

251
demandé sur CodeCaster 2011-09-06 12:52:56

13 réponses

courte, mauvaise réponse:

vous pouvez le faire en en manipulant l'événement beforeunload et en retournant une chaîne non-nulle :

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

le problème avec cette approche est que la soumission d'un formulaire déclenche aussi l'événement de déchargement . Ceci est corrigé facilement en ajoutant le drapeau que vous soumettez un formulaire:

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

appelant alors le setter lors de la soumission:

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>

mais lisez la suite...

réponse longue et correcte:

vous ne voulez pas non plus afficher ce message lorsque l'utilisateur n'a rien changé sur vos formulaires . Une solution consiste à utiliser l'événement beforeunload en combinaison avec un drapeau "dirty", qui ne déclenche l'invite que si elle est vraiment pertinente.

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

maintenant pour mettre en œuvre la méthode isDirty , il y a différentes approches.

Vous pouvez utiliser jQuery et la forme de la sérialisation , mais cette approche présente certains défauts. Tout d'abord, vous devez modifier le code pour travailler sur n'importe quelle forme ( $("form").each() le fera), mais le plus grand problème est que le serialize() de jQuery ne fonctionnera que sur les éléments nommés, non désactivés, donc changer un élément désactivé ou non ne déclenchera pas le drapeau sale. il y a des solutions de rechange pour que , comme faire des contrôles readonly au lieu d'activer, sérialiser puis désactiver à nouveau les commandes.

ainsi les événements semblent la voie à suivre. Vous pouvez essayer à l'écoute des touches . Cet événement a quelques numéros:

  • ne se déclenche pas sur les cases à cocher, les boutons radio, ou d'autres éléments qui sont modifiés par l'entrée de la souris.
  • déclenchera pour des touches non pertinentes comme la touche Ctrl .
  • ne déclenche pas les valeurs définies par le code JavaScript.
  • ne se déclenche pas en coupant ou en collant du texte à travers les menus de contexte.
  • ne fonctionnera pas pour les entrées virtuelles comme les lecteurs de données ou les checkbox/RadioButton beautifiers qui sauvegardent leur valeur dans une entrée cachée par JavaScript.

le change événement aussi ne déclenche pas sur les valeurs définies de Code JavaScript , donc ne fonctionnera pas non plus pour les entrées virtuelles.

liant l'événement input à tous input s (et textarea s et select s) sur votre page ne fonctionnera pas sur les navigateurs plus anciens et, comme toutes les solutions de gestion d'événements mentionnées ci-dessus, ne supporte pas undo. Quand un utilisateur change une boîte de texte et puis annule cela, ou vérifie et décochent une boîte de contrôle, le formulaire est toujours considéré comme sale.

et quand vous voulez mettre en œuvre plus de comportement, comme ignorer certains éléments, vous aurez encore plus de travail à faire.

Ne pas réinventer la roue:

donc avant de penser à mettre en œuvre ces solutions et toutes les solutions de rechange nécessaires, réalisez que vous réinventez la roue et que vous êtes enclin à rencontrer des problèmes que d'autres ont déjà résolus pour vous.

si votre application utilise déjà jQuery, vous pouvez aussi bien utiliser du code testé, maintenu au lieu de rouler le vôtre, et utiliser une bibliothèque de troisième partie pour tout cela. jquery's Êtes-vous sûr? plugin fonctionne très bien, voir leur page Démo . C'est aussi simple que cela:

<script src="jquery.are-you-sure.js"></script>

<script>
  $(function() {
    $('#myForm').areYouSure(
      {
        message: 'It looks like you have been editing something. '
               + 'If you leave before saving, your changes will be lost.'
      }
    );
  });

</script>

messages Personnalisés pas pris en charge partout

Ne remarque que Firefox 4 ne prend pas en charge les messages personnalisés dans cette boîte de dialogue. Depuis le mois dernier, Chrome 51 est en cours de déploiement dans lequel les messages personnalisés sont également supprimés .

quelques alternatives existent ailleurs sur ce site, mais je pense qu'un dialogue comme celui-ci est assez clair:

voulez-vous quitter ce site?

les modifications que vous avez faites ne peuvent pas être sauvegardées.

Quitter Séjour

435
répondu CodeCaster 2018-04-11 18:49:52

JavaScript onbeforeunload événement . Il est JavaScript non standard introduit par Microsoft, mais il fonctionne dans la plupart des navigateurs et leur documentation onbeforeunload a plus d'informations et d'exemples.

70
répondu Paul Annesley 2011-09-06 08:55:22

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; // i.e; if form state change show warning box, else don't show it.
});

vous pouvez Google jQuery form Serialize fonction, ce qui permettra de collecter toutes les entrées de formulaire et de l'enregistrer dans array. Je suppose que c'expliquer, c'est assez :)

28
répondu Wasim A. 2015-04-17 07:46:38

basé sur les réponses précédentes, et bricolé ensemble à partir de divers endroits dans le débordement de la pile, voici la solution que j'ai inventé qui gère le cas quand vous voulez réellement soumettre vos modifications:

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;

Il est intéressant de noter que IE11 semble exiger que le closeEditorWarning la fonction renvoie undefined pour ne pas afficher un message d'alerte.

7
répondu Jonathan 2014-05-09 14:32:52

la doublure suivante a fonctionné pour moi.

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

Juste mis modified à vrai ou false en fonction de l'état de votre demande.

5
répondu spencer.sm 2016-11-08 00:09:43

le code suivant fonctionne très bien. Vous devez atteindre les modifications d'entrée des éléments de votre formulaire via l'attribut 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; // i.e; if form state change show warning box, else don't show it.
            });
        });
4
répondu metzelder 2015-09-18 10:45:42
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;
3
répondu Ankit 2017-03-09 09:40:00

solution universelle ne nécessitant aucune configuration qui détecte automatiquement toutes les modifications d'entrée, y compris les éléments susceptibles d'être contenus:

"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;
    }
});
})();
2
répondu Eli Grey 2018-01-13 10:36:32

courte réponse:

let pageModified = true

window.addEventListener("beforeunload", 
  () => pageModified ? 'Close page without saving data?' : null
)
2
répondu Dan Key 2018-08-07 15:23:15

construit sur le dessus de Wasim A. excellente idée d'utiliser la sérialisation. Le problème était que l'avertissement était également affiché lorsque le formulaire était soumis. Ceci a été réparé ici.

var isSubmitting = false

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

il a été testé dans Chrome et IE 11.

2
répondu Eerik Sven Puudist 2018-09-27 18:16:37

ajoute à l'idée de @codecaster vous pouvez ajouter ceci à chaque page avec un formulaire (dans mon cas je l'utilise de manière globale donc seulement sur les formulaires auraient cet avertissement) changer sa fonction en

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

également mis sur les formulaires soumettre y compris l'ouverture de session et dans les liens de boutons d'annulation de sorte que lorsque la personne appuie sur Annuler ou soumettre le formulaire ne déclenchera pas l'avertissement aussi dans chaque page avec un formulaire...

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
1
répondu Esteban Fuentealba 2015-03-17 18:14:11

, Vous pouvez vérifier pour une explication détaillée ici: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close / comparaison des valeurs de forme-en charge et avant fermeture 151940920"

le code principal:

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
    }
1
répondu Kanika Sud 2015-06-11 10:02:44

tout d'abord, la plupart des navigateurs ont cette fonction par défaut. Et pourquoi avez-vous besoin? Pourquoi ne pas synchroniser le formulaire? Je veux dire, le sauvegarder sur n'importe quel changement sans attendre la soumission de l'utilisateur. Comme Google Contacts n'. Bien sûr, si seulement tous les champs du formulaire sont obligatoires. Les utilisateurs n'aiment pas quand ils forcent à remplir quelque chose sans la possibilité de partir pour penser s'ils en ont besoin. :)

-4
répondu YuS 2011-09-06 08:59:39