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

IT技术 javascript forms
2021-02-01 05:01:01

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

我如何才能保护表单,以便在有人离开或关闭浏览器选项卡时,应提示他们确认他们确实希望将表单保留为未保存的数据?

6个回答

简短的错误答案:

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

所以事件似乎是要走的路。您可以尝试听按键这个事件有几个问题:

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

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

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

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

不要重新发明轮子:

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

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

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

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

一个较旧的、目前已被废弃的项目,是jQuery 的你确定吗?plugin,它也很好用;查看他们的演示页面用法示例:

<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

请注意,在 Chrome 中不再使用自定义字符串;chromestatus.com/feature/5349061406228480
2021-03-16 05:01:01
是的,这在我的答案的最后一部分中有所描述。
2021-03-18 05:01:01
请注意,自 2014 年以来,jQuery areYouSure 已被放弃(Github上有 57 个未解决的问题),根据我的经验,它充满了错误,起初看起来它似乎有效,但经过一些快速测试后,我意识到它并不总是有效。根本不推荐这个
2021-03-23 05:01:01
@Chris 那是因为 Angular 正在操纵 DOM 来更改页面,而不是离开当前文档。
2021-03-31 05:01:01
@JacopoStanchi 我很遗憾没有找到。我的意思是我看起来很多 - 但这是在 1 月 10 日,所以有人可能写了一些东西,但我对此表示怀疑。你最好的办法是自己实现它——这不是你想听到的,但很抱歉。(但至少你不会像我使用 jQuery areYouSure 那样得到任何惊喜。)另外谁知道也许你甚至可以将它开源并且你的实现将与其他人共享;)
2021-03-31 05:01:01

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

我认为它现在已经成为标准。>> 该事件最初由 Microsoft 在 Internet Explorer 4 中引入,并在 HTML5 规范中标准化。
2021-03-14 05:01:01

通过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 函数,这将收集所有表单输入并将其保存在数组中。我想这个解释就足够了:)

如果它对您不起作用,请在此处检查:stackoverflow.com/questions/7080269/...
2021-03-21 05:01:01
不要使用#form 作为表单 id,因为 window 将创建一个名为 form 的对象:)
2021-03-27 05:01:01

无需配置即可自动检测所有输入修改的通用解决方案,包括 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);
    }
});
// clear modified inputs upon form submission
addEventListener("submit", (evt) => {
    modified_inputs.clear();
    // to prevent the warning from happening, it is advisable
    // that you clear your form controls back to their default
    // state with evt.target.reset() or form.reset() after submission
});
// warn before closing if any inputs are modified
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;
    }
});
})();
这在 Chrome 71.0.3578.98(官方版本)(64 位)上对我有用
2021-03-28 05:01:01
这非常有效,但是如果您将它与提交表单一起使用,则需要先清除 modified_inputs 设置,否则您将收到确认更改警报。我在我的表单中添加了一个事件监听器来调用提交,并且只运行 set 函数 clear,这样当 beforeunload 事件监听器运行时 modified_inputs 是清除的,并允许 post 继续。
2021-03-31 05:01:01
酷居然复制粘贴。谢谢你节省我的时间。在 Filfox 75.0 中为我工作
2021-03-31 05:01:01
喜欢这个片段
2021-04-05 05:01:01
凉豆。只需将此代码复制粘贴到您的测试页面中,进行更改并点击刷新即可。它将显示本机浏览器警报,警告您有未保存的更改。
2021-04-09 05:01:01

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

它已经在 Chrome 和 IE 11 中进行了测试。

我还使用了 id 而不是表单,它也可以工作。
2021-03-27 05:01:01
Wasim A 和 Eerik Sven Puudist - 谢谢我使用 id 作为表单和 id 作为我的保存按钮,因为我所有的按钮都是 type submit <form action="" id="marksform" method="POST"> <td><input type= "submit" name="submit_button" id="save" value="Save"></td> <td><input type="submit" name="submit_button" value="Next"></td> 和几个more all type submit 因此替换 .Submit( by specific click $('#save').click(function(){ isSubmitting = true }) 使用 '#marksform' 而不是 'form'
2021-04-08 05:01:01