一尘不染

在离开网页之前进行未保存的更改时警告用户

javascript

我的应用程序中有一些带有表单的页面。

如何以某种方式保护表单,如果有人离开或关闭浏览器选项卡,则应提示他们确认他们确实要保留未保存的数据?


阅读 1036

收藏
2020-04-25

共1个答案

一尘不染

简短错误的答案:

您可以通过处理beforeunload事件并返回非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.
});

这种方法的问题在于, 提交表单也会触发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()会这样做),但是最大的问题是jQueryserialize()只能在命名的,未禁用的元素上运行,因此更改任何禁用或未命名的元素都不会触发脏标志。有一些解决方法,例如将控件设置为只读而不是启用,序列化然后再次禁用控件。

因此,事件似乎是路要走。您可以尝试收听按键。此事件有几个问题:

  • 不会触发复选框,单选按钮或通过鼠标输入更改的其他元素。
  • 将触发不相关的按键,如Ctrl按键。
  • 不会触发通过JavaScript代码设置的值。
  • 通过上下文菜单剪切或粘贴文本不会触发。
  • 对于日期选择器或复选框/单选按钮美化器等虚拟输入(通过JavaScript将其值保存在隐藏输入中)不起作用。

change事件也[不会触发JavaScript代码设置的,因此也不适用于虚拟输入。

input事件绑定到页面上的所有inputs(以及textareas和selects)在[较旧的浏览器上将无法使用,并且像上述所有事件处理解决方案一样,不支持撤消。当用户更改文本框然后撤消该文本框,或者选中并取消选中一个复选框时,该表单仍被认为是脏表单。

当您想要实现更多的行为时,例如忽略某些元素,您将需要做更多的工作。

不要重新发明轮子:

因此,在考虑实施这些解决方案和所有必需的变通办法之前,请先意识到自己正在重新发明轮子,并且容易遇到其他人已经为您解决的问题。

如果您的应用程序已经使用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>

各地都不支持自定义消息

请注意,在此对话框中不支持自定义消息。从上个月开始,Chrome
51即将推出,其中自定义消息也将被删除。

本网站其他地方也有一些替代方法,但我认为这样的对话框很清楚:

您要离开这个网站吗?

您所做的更改可能不会保存。

Leave Stay

2020-04-25