在离开包含未保存更改的网页之前警告用户

2022-08-29 23:18:18

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

如何保护表单,以便如果有人离开或关闭浏览器选项卡,应提示他们确认是否确实要将未保存的数据留在表单中?


答案 1

简短错误的答案:

您可以通过处理 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.
});

此方法的问题在于,提交表单也会触发卸载事件。通过添加您要提交表单的标志,可以轻松解决此问题:

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

然后在提交时调用 setter:

<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 和表单序列化,但这种方法存在一些缺陷。首先,您必须更改代码以在任何形式上工作(将可以),但最大的问题是jQuery仅适用于命名的,非禁用的元素,因此更改任何已禁用或未命名的元素都不会触发脏标志。有一些解决方法,例如使控件只读,而不是启用,序列化,然后再次禁用控件。$("form").each()serialize()

因此,事件似乎是要走的路。您可以尝试侦听按键。此事件存在一些问题:

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

更改事件也不会在从 JavaScript 代码设置的值上触发,因此也不适用于虚拟输入。

输入事件绑定到页面上的所有输入s(以及 textareas 和 selects)在较旧的浏览器上不起作用,并且与上面提到的所有事件处理解决方案一样,不支持撤消。当用户更改文本框,然后撤消该文本框,或者选中并取消选中复选框时,表单仍被视为脏表单。

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

不要重新发明轮子:

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

如果您的应用程序已经使用jQuery,那么您也可以使用经过测试的,维护的代码,而不是滚动自己的代码,并使用第三方库来完成所有这些工作。

jquery.dirty(由@troseman在注释中建议)提供了用于正确检测表单是否已更改的功能,并防止用户在显示提示时离开页面。它还具有其他有用的功能,例如重置表单,以及将表单的当前状态设置为“干净”状态。用法示例:

$("#myForm").dirty({preventLeaving: true});

一个较旧的,目前被遗弃的项目是jQuery的Are You Sure?插件,它也很好用;查看他们的演示页面。用法示例:

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

并非所有地方都支持自定义消息

请注意,自2011年以来,Firefox 4在此对话框中不支持自定义消息。自 2016 年 4 月起,Chrome 51 正在推出,其中自定义消息也被删除

本网站其他地方存在一些替代方案,但我认为这样的对话已经足够清晰了:

你想离开这个网站吗?

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

Leave Stay


答案 2

查看 JavaScript onbeforeunload 事件。它是Microsoft引入的非标准JavaScript,但它适用于大多数浏览器,并且它们的onbeforeunload文档有更多信息和示例。