Javascript 在离开未保存更改的网页之前警告用户
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7317273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Warn user before leaving web page with unsaved changes
提问by Khan
I have some pages with forms in my application.
我的应用程序中有一些带有表单的页面。
How can I secure the form in such a way that if someone navigates away or closes the browser tab, they should be prompted to to confirm they really want to leave the form with unsaved data?
我如何才能保护表单,以便在有人离开或关闭浏览器选项卡时,应提示他们确认他们确实希望将表单保留为未保存的数据?
回答by CodeCaster
Short, wrong answer:
简短的错误答案:
You can do this by handling the beforeunload
event and returning a non-null string:
您可以通过处理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.
});
The problem with this approach is that submitting a form is also firing the unload event. This is fixed easily by adding the a flag that you're submitting a form:
这种方法的问题在于提交表单也会触发卸载事件。通过添加您正在提交表单的标志可以轻松解决此问题:
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.
});
};
Then calling the setter when submitting:
然后在提交时调用setter:
<form method="post" onsubmit="setFormSubmitting()">
<input type="submit" />
</form>
But read on...
但是请继续阅读...
Long, correct answer:
长而正确的答案:
You also don't want to show this message when the user hasn't changed anything on your forms. One solution is to use the beforeunload
event in combination with a "dirty" flag, which only triggers the prompt if it's really relevant.
当用户没有更改您的表单上的任何内容时,您也不希望显示此消息。一种解决方案是将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.
});
};
Now to implement the isDirty
method, there are various approaches.
现在要实现该isDirty
方法,有多种方法。
You can use jQuery and form serialization, but this approach has some flaws. First you have to alter the code to work on any form ($("form").each()
will do), but the greatest problem is that jQuery's serialize()
will only work on named, non-disabled elements, so changing any disabled or unnamed element will not trigger the dirty flag. There are workarounds for that, like making controls readonly instead of enabling, serializing and then disabling the controls again.
您可以使用jQuery 和表单序列化,但是这种方法有一些缺陷。首先,您必须更改代码以适用于任何形式($("form").each()
可以),但最大的问题是 jQueryserialize()
仅适用于命名的非禁用元素,因此更改任何禁用或未命名的元素都不会触发脏标志。有一些解决方法,例如将控件设为只读而不是启用、序列化然后再次禁用控件。
So events seem the way to go. You can try listening for keypresses. This event has a few issues:
所以事件似乎是要走的路。您可以尝试监听按键。这个事件有几个问题:
- Won't trigger on checkboxes, radio buttons, or other elements that are being altered through mouse input.
- Will trigger for irrelevant keypresses like the Ctrlkey.
- Won't trigger on values set through JavaScript code.
- Won't trigger on cutting or pasting text through context menus.
- Won't work for virtual inputs like datepickers or checkbox/radiobutton beautifiers which save their value in a hidden input through JavaScript.
- 不会触发复选框、单选按钮或通过鼠标输入更改的其他元素。
- 将触发不相关的按键,如Ctrl键。
- 不会触发通过 JavaScript 代码设置的值。
- 不会在通过上下文菜单剪切或粘贴文本时触发。
- 不适用于诸如日期选择器或复选框/单选按钮美化器之类的虚拟输入,它们通过 JavaScript 将其值保存在隐藏输入中。
The change
eventalso doesn't trigger on values set from JavaScript code, so also won't work for virtual inputs.
该change
事件也不会在从 JavaScript 代码设置的值上触发,因此也不适用于虚拟输入。
Binding the input
event to all input
s (and textarea
s and select
s)on your page won't work on older browsersand, like all event handling solutions mentioned above, doesn't support undo. When a user changes a textbox and then undoes that, or checks and unchecks a checkbox, the form is still considered dirty.
将input
事件绑定到页面上的所有input
s(以及textarea
s 和select
s)在旧浏览器上不起作用,并且与上面提到的所有事件处理解决方案一样,不支持撤消。当用户更改文本框然后撤消该操作,或选中和取消选中复选框时,表单仍被视为脏的。
And when you want to implement more behavior, like ignoring certain elements, you'll have even more work to do.
当您想要实现更多行为时,例如忽略某些元素,您将有更多工作要做。
Don't reinvent the wheel:
不要重新发明轮子:
So before you think about implementing those solutions and all required workarounds, realize you're reinventing the wheel and you're prone to running into problems others have already solved for you.
因此,在您考虑实施这些解决方案和所有必需的变通方法之前,请意识到您正在重新发明轮子,并且您很容易遇到其他人已经为您解决的问题。
If your application already uses jQuery, you may as well use tested, maintained code instead of rolling your own, and use a third-part library for all of this. jQuery's Are You Sure? pluginworks great, see their demo page. It's as simple as this:
如果您的应用程序已经使用 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>
Custom messages not supported everywhere
并非所有地方都支持自定义消息
Do note that Firefox 4didn't support custom messages in this dialog. As of april 2016, Chrome 51 is being rolled out in which custom messages are also being removed.
请注意,Firefox 4不支持此对话框中的自定义消息。截至 2016 年 4 月,Chrome 51 正在推出,其中自定义消息也被删除。
Some alternatives exist elsewhere on this site, but I think a dialog like this is clear enough:
本网站的其他地方存在一些替代方案,但我认为这样的对话已经足够清楚了:
Do you want to leave this site?
Changes you made may not be saved.
LeaveStay
你想离开这个网站吗?
您所做的更改可能无法保存。
LeaveStay
回答by Paul Annesley
Check out the JavaScript onbeforeunload event. It's non-standard JavaScript introduced by Microsoft, however it works in most browsers and their onbeforeunload documentationhas more information and examples.
查看 JavaScript onbeforeunload 事件。它是 Microsoft 引入的非标准 JavaScript,但它适用于大多数浏览器,其 onbeforeunload 文档包含更多信息和示例。
回答by Wasim A.
via jquery
通过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.
});
You can Google JQuery Form Serialize function, this will collect all form inputs and save it in array. I guess this explain is enough :)
您可以使用 Google JQuery Form Serialize 函数,这将收集所有表单输入并将其保存在数组中。我想这个解释就足够了:)
回答by Eli Grey
Universal solution requiring no configuration that automatically detects all input modification, including contenteditable elements:
无需配置即可自动检测所有输入修改的通用解决方案,包括 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;
}
});
})();
回答by Eerik Sven Puudist
Built on top of Wasim A.'sexcellent idea to use serialization. The problem there was that the warning was also shown when the form was being submitted. This has been fixed here.
建立在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.'
}
});
})
It has been tested in Chrome and IE 11.
它已经在 Chrome 和 IE 11 中进行了测试。
回答by Jonathan
Based on the previous answers, and cobbled together from various places in stack overflow, here is the solution I came up with which handles the case when you actually want to submit your changes:
根据之前的答案,并从堆栈溢出的各个地方拼凑起来,这是我想出的解决方案,它可以处理您实际想要提交更改的情况:
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;
It's worth noting that IE11 seems to require that the closeEditorWarning
function returns undefined
for it not to show an alert.
值得注意的是,IE11 似乎要求该closeEditorWarning
函数返回undefined
以使其不显示警报。
回答by spencer.sm
The following one-liner has worked for me.
以下单线对我有用。
window.onbeforeunload = s => modified ? "" : null;
Just set modified
to trueor falsedepending on the state of your application.
只需根据应用程序的状态设置modified
为true或false 即可。
回答by metzelder
Following code works great. You need to reach your form elements' input changes via id attribute:
以下代码效果很好。您需要通过 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.
});
});
回答by Ankit
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;
回答by Vennila M
You can use serialize() to create a URL encoded text string by serializing form values and check whether the form has changed beforeunload
您可以使用 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?'
}
})
Refer this link https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-formWritten by Vladimir Sidorenko
请参阅此链接https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form由 Vladimir Sidorenko 撰写