Ostrzegaj użytkownika przed opuszczeniem strony internetowej z niezapisanymi zmianami

Mam kilka stron z formularzami w mojej aplikacji.

Jak zabezpieczyć formularz w taki sposób, że jeśli ktoś przejdzie lub zamknie kartę przeglądarki, powinien zostać poproszony o potwierdzenie, że naprawdę chce opuścić formularz z niezapisanymi danymi?

Author: CodeCaster, 2011-09-06

13 answers

Krótka, zła odpowiedź:

Możesz to zrobić poprzez obsługę zdarzenia beforeunload i zwrócenie non-null string :

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

Problem z tym podejściem polega na tym, że wysłanie formularza jest również wywołaniem zdarzenia rozładowania . Można to łatwo naprawić, dodając flagę a, którą przesyłasz formularz:

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

Następnie wywołanie settera podczas składania:

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>
[[17]} ale czytaj dalej...

Długa, poprawna odpowiedź:

Ty też nie chcesz pokazywać ta wiadomość , gdy użytkownik nie zmienił niczego w formularzach . Jednym z rozwiązań jest użycie zdarzenia beforeunload w połączeniu z flagą "dirty", która uruchamia monit tylko wtedy, gdy jest naprawdę istotna.

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

Teraz, aby wdrożyć metodę isDirty, istnieją różne podejścia.

Możesz używać jQuery i serializacji formularzy, ale takie podejście ma pewne wady. Najpierw musisz zmienić kod, aby działał na dowolnej formie ($("form").each() zrobi), ale największym problemem jest serialize() jQuery będzie działać tylko na nazwanych, nieaktywnych elementach, więc zmiana dowolnego wyłączonego lub nienazwanego elementu nie spowoduje wywołania brudnej flagi. istnieją obejścia dla tego, takie jak wprowadzanie kontrolek tylko do odczytu zamiast włączania, serializowanie, a następnie ponowne wyłączanie kontrolek.

Więc wydarzenia wydają się iść drogą. Możesz spróbować nasłuchać klawiszy. To wydarzenie ma kilka problemów:

  • nie uruchamia się przy polach wyboru, przyciskach opcji ani innych elementach które są zmieniane poprzez wejście myszy.
  • wywoła nieistotne naciśnięcia klawiszy, takie jak Ctrl.
  • nie uruchomi się na wartościach ustawionych za pomocą kodu JavaScript.
  • nie uruchamia się przy wycinaniu lub wklejaniu tekstu przez menu kontekstowe.
  • nie będzie działać dla wirtualnych wejść, takich jak datepickers lub checkbox/radiobutton upiększaczy, które zapisują swoją wartość w ukrytym wejściu przez JavaScript.

The change event również nie uruchamia na wartościach ustawionych z kodu JavaScript , więc nie będzie działać również dla wejść wirtualnych.

Powiązanie zdarzenia input ze wszystkimi zdarzeniami input S (oraz textareaS I select S) na twojej stronie nie będzie działać na starszych przeglądarkach i, podobnie jak wszystkie rozwiązania obsługi zdarzeń wymienione powyżej, nie obsługuje cofania. Gdy użytkownik zmienia pole tekstowe, a następnie je wycofuje lub sprawdza i odznacza pole wyboru, formularz nadal jest uważany za brudny.

I kiedy chcesz zaimplementować więcej zachowań, jak ignorując pewne elementy, będziesz miał jeszcze więcej pracy.

Nie odkrywaj koła na nowo:

Więc zanim pomyślisz o wdrożeniu tych rozwiązań i wszystkich wymaganych obejść, zdaj sobie sprawę, że odkrywasz koło na nowo i jesteś podatny na problemy, które inni już rozwiązali za Ciebie.

Jeśli Twoja aplikacja już korzysta z jQuery, możesz równie dobrze użyć sprawdzonego, utrzymanego kodu zamiast zwijania własnego, i użyć trzeciej części biblioteki do tego wszystkiego. jQuery na pewno? plugin działa świetnie, zobacz stronę demo . To takie proste:

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

Niestandardowe wiadomości nie są obsługiwane wszędzie

Należy pamiętać, że Firefox 4 nie obsługiwał niestandardowych wiadomości w tym oknie dialogowym. Od ostatniego miesiąca jest rozwijany Chrome 51 , w którym usuwane są również niestandardowe wiadomości .

Niektóre alternatywy istnieją gdzie indziej na tej stronie, ale myślę, że okno dialogowe takie jak to jest jasne wystarczy:

Chcesz opuścić tę stronę?

Wprowadzone zmiany mogą nie zostać zapisane.

Zostawić Stay

 435
Author: CodeCaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 18:49:52

Sprawdź Zdarzenie JavaScript onbeforeunload. Jest to Niestandardowy JavaScript wprowadzony przez Microsoft, jednak działa w większości przeglądarek i ich dokumentacja onbeforeunload zawiera więcej informacji i przykładów.

 70
Author: Paul Annesley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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.
});

Możesz Google jQuery formularz Serialize funkcji, to będzie zbierać wszystkie wejścia formularza i zapisać go w tablicy. To chyba wystarczy:)

 28
Author: Wasim A.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-17 07:46:38

Bazując na poprzednich odpowiedziach i ułożonych razem z różnych miejsc w stack overflow, oto rozwiązanie, które wymyśliłem, które obsługuje sprawę, gdy faktycznie chcesz przesłać swoje zmiany:

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;

Warto zauważyć, że IE11 wydaje się wymagać, aby funkcja closeEditorWarning zwracała undefined, aby nie pokazywała alarmu.

 7
Author: Jonathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-09 14:32:52

Następujący one-liner działa dla mnie.

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

Ustaw modifiedna truelub false w zależności od stanu aplikacji.

 5
Author: spencer.sm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 00:09:43

Następujący kod działa świetnie. Musisz wprowadzić zmiany w elementach formularza poprzez atrybut 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
Author: metzelder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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
Author: Ankit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 09:40:00

Uniwersalne rozwiązanie nie wymagające konfiguracji, które automatycznie wykrywa wszystkie modyfikacje wejścia, w tym elementy 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;
    }
});
})();
 2
Author: Eli Grey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-13 10:36:32

Krótka odpowiedź:

let pageModified = true

window.addEventListener("beforeunload", 
  () => pageModified ? 'Close page without saving data?' : null
)
 2
Author: Dan Key,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 15:23:15

Zbudowany na bazie Wasima A. doskonały pomysł użycia serializacji. Problem polegał na tym, że Ostrzeżenie było również wyświetlane podczas składania formularza. To zostało naprawione tutaj.

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

Został przetestowany w Chrome i IE 11.

 2
Author: Eerik Sven Puudist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 18:16:37

Dodanie do pomysłu @ codecaster możesz to dodać do każdej strony z formularzem (w moim przypadku używam go w sposób globalny, więc tylko na formularzach będzie to Ostrzeżenie) zmień jego funkcję na

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

Również umieścić na formularzach submit łącznie z loginem i w przyciskach cancel linki więc po naciśnięciu przycisku cancel lub submit formularz nie uruchomi Ostrzeżenia również na każdej stronie w formularzu...

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
 1
Author: Esteban Fuentealba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 18:14:11

Możesz sprawdzić szczegółowe wyjaśnienie tutaj: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close / porównanie-form-values-on-load-and-before-close

Główny kod:

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
Author: Kanika Sud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-11 10:02:44

Przede wszystkim większość przeglądarek domyślnie posiada tę funkcję. I po co ci to w ogóle? Dlaczego nie synchronizować formularza? To znaczy, zapisz go na każdej zmianie bez czekania na zgłoszenie od użytkownika. Tak jak robią to Kontakty Google. Oczywiście jeśli tylko wszystkie pola w formularzu są obowiązkowe. Użytkownicy nie lubią, gdy zmuszają do wypełnienia czegoś bez możliwości odejścia, aby pomyśleć, czy tego potrzebują. :)

 -4
Author: YuS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-06 08:59:39