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

Kha*_*han 314 javascript forms

我的应用程序中有一些表单.

如何以这样的方式保护表单:如果有人导航或关闭浏览器选项卡,应提示他们确认他们确实要保留未保存数据的表单?

Cod*_*ter 527

简短的错误答案:

您可以通过处理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.
});
Run Code Online (Sandbox Code Playgroud)

这种方法的问题是提交表单也会触发卸载事件.通过添加您提交表单的标志,可以轻松修复此问题:

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.
    });
};
Run Code Online (Sandbox Code Playgroud)

然后在提交时调用setter:

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>
Run Code Online (Sandbox Code Playgroud)

但请继续阅读......

长而正确的答案:

当用户未在表单上更改任何内容时,您也不希望显示此消息.一种解决方案是将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.
    });
};
Run Code Online (Sandbox Code Playgroud)

现在实现该isDirty方法,有各种方法.

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

所以事件似乎要走了.你可以尝试听按键.此事件有一些问题:

  • 不会触发复选框,单选按钮或通过鼠标输入更改的其他元素.
  • 会触发像Ctrl钥匙这样无关紧要的按键.
  • 不会触发通过JavaScript代码设置的值.
  • 不会通过上下文菜单触发剪切或粘贴文本.
  • 不适用于像datepickers或checkbox/radiobutton美化器这样的虚拟输入,它们通过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>
Run Code Online (Sandbox Code Playgroud)

自定义消息不受支持

请注意,Firefox 4在此对话框中不支持自定义消息.截至上个月,Chrome 51正在推出,其中也会删除自定义消息.

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

你想离开这个网站吗?

您所做的更改可能无法保存.

Leave Stay

  • 请注意jQuery areYouSure自2014年以来已被放弃([Github]上的57个未解决的问题(https://github.com/codedance/jquery.AreYouSure/issues)),它充满了我的经验中的错误,它可能看起来像是有效的起初,经过一些快速测试,我意识到它并不总是有效.根本不会推荐这个 (11认同)
  • 请注意,使用自定义字符串在Chrome中不再有效; https://www.chromestatus.com/feature/5349061406228480 (6认同)
  • 是的,这在我的答案的最后一节中描述. (5认同)
  • @JacopoStanchi我很遗憾没找到任何.而且我的意思是我看了很多 - 但这是在1月10日,所以有人可能写了一些但我怀疑它.你最好的选择是自己实现它 - 这不是你想要听到的那么抱歉.(但至少你不会像我使用jQuery那样得到任何惊喜.你知道也许你知道也许你甚至可以把它开源,你的实现将与其他人共享;) (4认同)
  • @Chris,因为Angular正在操纵DOM来改变页面,而不是导航离开当前文档. (2认同)

Pau*_*ley 75

查看JavaScript onbeforeunload事件.它是Microsoft引入的非标准JavaScript,但它适用于大多数浏览器,其onbeforeunload文档中包含更多信息和示例.

  • 我认为这已成为标准。&gt;&gt;该事件最初由Microsoft在Internet Explorer 4中引入,并在HTML5规范中进行了标准化。 (2认同)

Was*_* A. 34

通过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.
});
Run Code Online (Sandbox Code Playgroud)

您可以使用Google JQuery Form Serialize功能,这将收集所有表单输入并将其保存在数组中.我想这个解释就够了:)

  • 不要使用#form作为表单ID,因为窗口将创建和对象命名形式:) (6认同)
  • 如果它不适合您,请在此处检查:http://stackoverflow.com/questions/7080269/javascript-before-leaving-the-page/21061009#comment41813429_21061009 (2认同)

Eli*_*rey 12

通用解决方案,无需自动检测所有输入修改的配置,包括可满足的元素:

"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;
    }
});
})();
Run Code Online (Sandbox Code Playgroud)

  • 凉豆子。只需将此代码复制粘贴到您的测试页中,进行更改并点击刷新即可。它将显示本机浏览器警报,警告您有未保存的更改。 (3认同)
  • 这对我在 Chrome 71.0.3578.98(官方版本)(64 位)上有效 (2认同)
  • 这非常有效,但如果您将其与提交表单一起使用,则需要先清除modified_inputs集,否则您将收到确认更改警报。我在表单中添加了一个事件侦听器以在提交时调用,然后只运行 set 函数clear,这样当 beforeunload 事件侦听器运行时,modified_inputs 就会被清除,并允许 post 继续。 (2认同)

Eer*_*ist 10

建立在Wasim A的基础之上.使用序列化绝佳主意.问题是在提交表单时也显示了警告.这已在此处修复.

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.'
        }
    });
})
Run Code Online (Sandbox Code Playgroud)

它已在Chrome和IE 11中测试过.

  • Wasim A 和 Eerik Sven Puudist - 谢谢,我使用了表单 id 和保存按钮 id,因为我的所有按钮都是类型提交 &lt;form action="" id="marksform" method="POST"&gt; &lt;td&gt;&lt;input type= “提交” name="submit_button" id="save" value="Save"&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="submit" name="submit_button" value="Next"&gt;&lt;/td&gt; 和一些更多所有类型提交 因此替换 .Submit( 通过特定点击 $('#save').click(function(){ isSubmitting = true }) 使用 '#marksform' 而不是 'form' (2认同)

Jon*_*han 7

基于之前的答案,并从堆栈溢出的各个位置拼凑在一起,这是我想出的解决方案,当你真正想要提交你的更改时:

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;
Run Code Online (Sandbox Code Playgroud)

值得注意的是IE11似乎要求closeEditorWarning函数返回undefined它不显示警报.


spe*_*.sm 6

以下单行为我工作.

window.onbeforeunload = s => modified ? "" : null;
Run Code Online (Sandbox Code Playgroud)

根据应用程序的状态设置modifiedtruefalse.

  • 一些建议:返回自定义消息而不是空字符串,因为某些浏览器会显示该消息(例如Edge),并返回undefined而不是null,因为IE如果print修改为false时将显示null。 (2认同)

小智 5

您可以使用 serialize() 通过序列化表单值来创建 URL 编码的文本字符串,并在卸载之前检查表单是否已更改

$(document).ready(function(){
    var form = $('#some-form'),
        original = form.serialize()

    form.submit(function(){
        window.onbeforeunload = null
    })

    window.onbeforeunload = function(){
        if (form.serialize() != original)
            return 'Are you sure you want to leave?'
    }
})
Run Code Online (Sandbox Code Playgroud)

请参阅此链接https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form 由 Vladimir Sidorenko 撰写