Предупредить пользователя перед тем, как покинуть веб-страницу с несохраненными изменениями - PullRequest
290 голосов
/ 06 сентября 2011

У меня есть несколько страниц с формами в моем приложении.

Как я могу защитить форму таким образом, чтобы, если кто-то уходит или закрывает вкладку браузера, ему предлагалось подтвердить, что он действительно хочет оставить форму с несохраненными данными?

Ответы [ 15 ]

499 голосов
/ 06 сентября 2011

Короткий, неправильный ответ:

Это можно сделать с помощью обработки события beforeunload и возврата ненулевой строки :

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

Проблемапри таком подходе отправка формы также запускает событие unload .Это легко исправить, добавив флаг, что вы отправляете форму:

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

Затем вызываете сеттер при отправке:

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

Но читайте дальше ...

Длинный правильный ответ:

Вы также не хотите показывать это сообщение , когда пользователь ничего не изменил в ваших формах .Одним из решений является использование события beforeunload в сочетании с флагом «грязный», который вызывает запрос только в том случае, если он действительно актуален.

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

Теперь для реализации метода isDirty существуют различные подходы.

Вы можете использовать jQuery и сериализовать формы , но у этого подхода есть некоторые недостатки.Сначала нужно изменить код для работы с любой формой (подойдет $("form").each()), но самая большая проблема в том, что serialize() в jQuery будет работать только с именованными, не отключенными элементами, поэтому изменение любого отключенного или безымянного элемента не будетвызвать грязный флаг. Есть обходные пути для этого , такие как создание элементов управления только для чтения вместо включения, сериализации и повторного отключения элементов управления.

Таким образом, события кажутся подходящими.Вы можете попробовать прослушивание нажатий клавиш .Это событие имеет несколько проблем:

  • Не будет запускаться на флажках, переключателях или других элементах, которые изменяются с помощью мыши.
  • Будет срабатывать для неактуальных нажатий клавиш, таких как Ctrl .
  • Не сработает при значениях, заданных с помощью кода JavaScript.
  • Не сработает при вырезании или вставке текста через контекстные меню.
  • Не будет работать для виртуальных входов, таких как датчики или флажки / кнопки-переключатели, которые сохраняют свои значения в скрытом вводе через JavaScript.

Событие change также не срабатывает при значениях, заданных из кода JavaScript , поэтому также не работает для виртуальных входов.

Привязка события input ко всем input с (и textarea с и select с) на вашей странице не будет работать в старых браузерах и, как и все упомянутые выше решения для обработки событий, не поддерживает отмену.Когда пользователь изменяет текстовое поле, а затем отменяет его, или устанавливает и снимает флажок, форма все еще считается грязной.

А когда вы хотите реализовать больше поведения, например, игнорировать определенные элементы, вы будете иметь дажебольше работы.

Не изобретайте колесо:

Итак, прежде чем задуматься о реализации этих решений и всех необходимых обходных путей, поймите, что вы заново изобретаете колесо и вы склонны кстолкнуться с проблемами, которые другие уже решили для вас.

Если ваше приложение уже использует jQuery, вы также можете использовать проверенный, поддерживаемый код вместо собственного и использовать библиотеку третьей части для всего этого. jQuery, вы уверены?плагин отлично работает, смотрите демонстрационную страницу .Это так просто:

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

Настраиваемые сообщения не поддерживаются везде

Обратите внимание, что Firefox 4 не поддерживает настраиваемые сообщения в этом диалоговом окне.По состоянию на прошлый месяц Chrome 51 выпускается , в котором также удаляются пользовательские сообщения .

Некоторые альтернативы существуют на этом сайте, но я думаю, что такой диалог достаточно ясен:

Вы хотите покинуть этот сайт?

Изменения, сделанные вами, могут быть не сохранены.

Оставить Оставаться

74 голосов
/ 06 сентября 2011

Проверьте событие JavaScript onbeforeunload . Это нестандартный JavaScript, представленный Microsoft, однако он работает в большинстве браузеров, и их документация onbeforeunload содержит больше информации и примеров.

33 голосов
/ 11 января 2014

через 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.
});

Вы можете использовать функцию Google JQuery Form Serialize, которая соберет все входные данные формы и сохранит их в массиве. Я думаю, этого объяснения достаточно:)

11 голосов
/ 13 января 2018

Универсальное решение, не требующее настройки, которое автоматически обнаруживает все изменения ввода, включая 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;
    }
});
})();
7 голосов
/ 27 сентября 2018

Построенный поверх Отличная идея Васима А. использовать сериализацию. Проблема заключалась в том, что предупреждение также отображалось при отправке формы. Это было исправлено здесь.

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

Он был протестирован в Chrome и IE 11.

7 голосов
/ 03 декабря 2013

Основываясь на предыдущих ответах и ​​собранных вместе из разных мест переполнения стека, вот решение, которое я придумал, которое обрабатывает случай, когда вы действительно хотите отправить свои изменения:

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;

Стоит отметить, что IE11, похоже, требует, чтобы функция closeEditorWarning возвращала undefined, чтобы не показывать предупреждение.

6 голосов
/ 08 ноября 2016

У меня сработал следующий однострочный.

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

Просто установите modified на true или false в зависимости от состояния вашего приложения.

4 голосов
/ 18 сентября 2015

Следующий код прекрасно работает. Вам нужно достичь изменений ввода элементов формы через атрибут 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.
            });
        });
3 голосов
/ 08 июля 2014
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;
1 голос
/ 07 февраля 2019

Решение от Eerik Sven Puudist ...

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

... спонтанно выполнило мою работу в сложной объектно-ориентированной обстановке без каких-либо изменений.

Единственноеизменение, которое я применил, заключалось в том, чтобы ссылаться на конкретную форму (только одну форму на файл), называемую «formForm» («form» -> «#formForm»):

<form ... id="formForm" name="formForm" ...>

Особенно хорошо то, чтоКнопка отправки «оставлена ​​одна».

Кроме того, она работает и для меня с последней версией Firefox (по состоянию на 7 февраля 2019 года).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...