Javascript 在离开带有未保存更改的网页之前警告用户
我的申请表上有一些表格 我如何确保表单的安全性?如果有人离开或关闭了浏览器选项卡,则应提示他们确认是否确实要将未保存的数据保留在表单中?查看JavaScript。这是微软推出的非标准JavaScript,但它在大多数浏览器中都能工作,并且有更多的信息和示例。简短的错误答案: 您可以通过以下方式完成此操作: 这种方法的问题是,提交表单也会触发卸载事件。通过添加提交表单的a标志,可以轻松解决此问题:Javascript 在离开带有未保存更改的网页之前警告用户,javascript,forms,Javascript,Forms,我的申请表上有一些表格 我如何确保表单的安全性?如果有人离开或关闭了浏览器选项卡,则应提示他们确认是否确实要将未保存的数据保留在表单中?查看JavaScript。这是微软推出的非标准JavaScript,但它在大多数浏览器中都能工作,并且有更多的信息和示例。简短的错误答案: 您可以通过以下方式完成此操作: 这种方法的问题是,提交表单也会触发卸载事件。通过添加提交表单的a标志,可以轻松解决此问题: var formSubmitting = false; var setFormSubmitting
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>
现在要实现isDirty
方法,有多种方法
您可以使用,但这种方法有一些缺陷。首先,您必须修改代码以处理任何表单($(“表单”)。each()
都可以,但最大的问题是jQuery的serialize()
只能处理命名的、非禁用的元素,因此更改任何禁用或未命名的元素都不会触发脏标记,例如将控件设置为只读而不是启用,序列化然后再次禁用控件
因此,事件似乎是前进的方向。你可以试试。本次活动有几个问题:
- 不会在复选框、单选按钮或其他通过鼠标输入更改的元素上触发
- 将触发不相关的按键,如Ctrl键
- 不会在通过JavaScript代码设置的值上触发
- 通过上下文菜单剪切或粘贴文本时不会触发
- 不适用于诸如日期选择器或复选框/单选按钮美化器之类的虚拟输入,它们通过JavaScript将值保存在隐藏输入中
<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>
$(函数(){
$(“#我的表格”)。你确定吗(
{
消息:“看起来你正在编辑一些东西。”
+'如果您在保存之前离开,您所做的更改将丢失。'
}
);
});
自定义消息在任何地方都不受支持
请注意,此对话框中不支持自定义消息。截至2016年4月,Chrome 51正在推出
在这个网站的其他地方存在一些替代方案,但我认为这样的对话已经足够清晰:
是否要离开此网站?
您所做的更改可能无法保存
留下来
首先,大多数浏览器默认都有这个功能。
你为什么需要这个?为什么不保持表单同步?我的意思是,保存在任何更改上,无需等待用户提交。就像谷歌联系人一样。当然,如果表单中的所有字段都是必填字段。用户不喜欢在没有机会离开去思考是否需要的情况下,强行加满东西。:) 根据前面的答案,并从stack overflow中的不同位置拼凑而成,我提出了一个解决方案,当您确实想要提交更改时,它会处理这个问题:
window.thisPage=window.thisPage | |{};
window.thisPage.isDirty=false;
window.thisPage.closeEditorWarning=函数(事件){
if(window.thisPage.isDirty)
return“看起来您正在编辑某些内容”+
“-如果在保存前离开,则更改将丢失。”
其他的
返回未定义;
};
$(“form”)。在('keyup','textarea',//您也可以在此处使用输入[type=text]。
函数(){
window.thisPage.isDirty=true;
});
$(“表格”)。提交(函数(){
QC.thisPage.isDirty=false;
});
window.onbeforeunload=window.thisPage.closeEditorWarning;
值得注意的是,IE11似乎要求closeEditorWarning
函数返回undefined
,以使其不显示警报。通过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表单序列化函数,这将收集所有表单输入并将其保存在数组中。我想这个解释已经足够了:)增加了@codecaster的概念 您可以将其添加到具有表单的每个页面(在我的示例中,我以全局方式使用它,因此只有在表单上才会有此警告)将其函数更改为
if ( formSubmitting || document.getElementsByTagName('form').length == 0)
此外,在表单提交中加入登录和取消按钮链接,这样当用户按取消或提交表单时,表单不会在没有表单的每个页面中触发警告
<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
您可以在此处查看详细说明: 加载和关闭前的形状值比较 主要代码:
function formCompare(defaultValues, valuesOnClose) {
// Create arrays of property names
var aPropsFormLoad = Object.keys(defaultValues);
var aPropsFormClose = Object.keys(valuesOnClose);
// If number of properties is different,
// objects are not equivalent
if (aPropsFormLoad.length != aPropsFormClose.length) {
return false;
}
for (var i = 0; i < aPropsFormLoad.length; i++) {
var propName = aPropsFormLoad[i];
// If values of same property are not equal,
// objects are not equivalent
if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
//add polyfill for older browsers, as explained on the link above
//use the block below on load
for(i=0; i < document.forms[0].elements.length; i++){
console.log("The field name is: " + document.forms[0].elements[i].name +
" and it’s value is: " + document.forms[0].elements[i].value );
aPropsFormLoad[i] = document.forms[0].elements[i].value;
}
//create a similar array on window unload event.
//and call the utility function
if (!formCompare(aPropsOnLoad, aPropsOnClose))
{
//perform action:
//ask user for confirmation or
//display message about changes made
}
函数formCompare(默认值,值关闭){
//创建属性名称数组
var aPropsFormLoad=Object.keys(默认值);
var aPropsFormClose=Object.keys(valuesOnClose);
//如果属性的数量不同,
//对象不是等价的
if(aPropsFormLoad.length!=aPropsFormClose.length){
返回false;
}
对于(变量i=0;iif ( formSubmitting || document.getElementsByTagName('form').length == 0)
<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
function formCompare(defaultValues, valuesOnClose) {
// Create arrays of property names
var aPropsFormLoad = Object.keys(defaultValues);
var aPropsFormClose = Object.keys(valuesOnClose);
// If number of properties is different,
// objects are not equivalent
if (aPropsFormLoad.length != aPropsFormClose.length) {
return false;
}
for (var i = 0; i < aPropsFormLoad.length; i++) {
var propName = aPropsFormLoad[i];
// If values of same property are not equal,
// objects are not equivalent
if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
//add polyfill for older browsers, as explained on the link above
//use the block below on load
for(i=0; i < document.forms[0].elements.length; i++){
console.log("The field name is: " + document.forms[0].elements[i].name +
" and it’s value is: " + document.forms[0].elements[i].value );
aPropsFormLoad[i] = document.forms[0].elements[i].value;
}
//create a similar array on window unload event.
//and call the utility function
if (!formCompare(aPropsOnLoad, aPropsOnClose))
{
//perform action:
//ask user for confirmation or
//display message about changes made
}
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.
});
});
window.onbeforeunload = s => modified ? "" : null;
"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;
}
});
})();
let pageModified = true
window.addEventListener("beforeunload",
() => pageModified ? 'Close page without saving data?' : null
)
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.'
}
});
})
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.'
}
});
})
<form ... id="formForm" name="formForm" ...>
'use strict';
(() => {
const modified_inputs = new Set();
const defaultValue = 'defaultValue';
// store default values
addEventListener('beforeinput', evt => {
const target = evt.target;
if (!(defaultValue in target.dataset)) {
target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener('input', evt => {
const target = evt.target;
let original = target.dataset[defaultValue];
let current = ('' + (target.value || target.textContent)).trim();
if (original !== current) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener(
'saved',
function(e) {
modified_inputs.clear()
},
false
);
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;
}
});
})();
'use strict';
(() => {
const modified_inputs = new Set();
const defaultValue = 'defaultValue';
// store default values
addEventListener('beforeinput', evt => {
const target = evt.target;
if (!(defaultValue in target.dataset)) {
target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener('input', evt => {
const target = evt.target;
let original = target.dataset[defaultValue];
let current = ('' + (target.value || target.textContent)).trim();
if (original !== current) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
if(modified_inputs.size){
const event = new Event('needSave')
window.dispatchEvent(event);
}
});
addEventListener(
'saved',
function(e) {
modified_inputs.clear()
},
false
);
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;
}
});
const ua = navigator.userAgent.toLowerCase();
if(/MicroMessenger/i.test(ua)) {
let pushed = false
addEventListener('needSave', evt => {
if(!pushed) {
pushHistory();
window.addEventListener("popstate", function(e) {
if(modified_inputs.size) {
var cfi = confirm('确定要离开当前页面嘛?' + JSON.stringify(e));
if (cfi) {
modified_inputs.clear()
history.go(-1)
}else{
e.preventDefault();
e.stopPropagation();
}
}
}, false);
}
pushed = true
});
}
function pushHistory() {
var state = {
title: document.title,
url: "#flag"
};
window.history.pushState(state, document.title, "#flag");
}
})();
$(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?'
}
})
<input type="text" name="field" value="" class="onchange" />
var ischanged = false;
$('.onchange').change(function () {
ischanged = true;
});
window.onbeforeunload = function (e) {
if (ischanged) {
return "Make sure to save all changes.";
}
};
var isChanged = false;
var isSubmit = false;
window.onbeforeunload = function (e) {
if (isChanged && (!isSubmit)) {
return "Make sure to save all changes.";
}
};
$('#submitbutton').click(function () {
isSubmit = true;
});
$('.onchange').change(function () {
isChanged = true;
});
/* Dirty warning for forms */
dirty = (skipHiddenOrNullToInit) => {
/* will return True if there are changes in form(s)
for first initialization you can use both: .dirty(null) or .dirty() (ignore its result)
.dirty(null) will (re)initialize all controls - in addititon use it after Save if you stay on same page
.dirty() will initialize new controls - in addititon use it if you add new fields with JavaScript
then
.dirty() (or: .dirty(false)) says if data are changed without regard to hidden fields
.dirty(true) says if data are changed with regard to hidden fields (ie. fields with .d-none or .hidden class)
controls with .ignoreDirty class will be skipped always
previous about .d-none, .hidden, .ignoreDirty applies to the control itself and all its ancestors
*/
let isDirty = false;
let skipSelectors = '.ignoreDirty';
if (skipHiddenOrNullToInit) {
skipSelectors += ', .d-none, .hidden'
} else if (skipHiddenOrNullToInit === undefined) {
skipHiddenOrNullToInit = false;
}
$('input, select').each(
function(_idx, el) {
if ($(el).prop('type') !== 'hidden') {
let dirtyInit = $(el).data('dirty-init');
if (skipHiddenOrNullToInit === null || dirtyInit === undefined) {
try {
isChromeAutofillEl = $(el).is(":-webkit-autofill");
} catch (error) {
isChromeAutofillEl = false;
}
if (isChromeAutofillEl && $(el).data('dirty-init') === undefined) {
setTimeout(function() { // otherwise problem with Chrome autofilled controls
$(el).data('dirty-init', $(el).val());
}, 200)
} else {
$(el).data('dirty-init', $(el).val());
}
} else if ($(el).closest(skipSelectors).length === 0 && dirtyInit !== $(el).val()) {
isDirty = true;
return false; // breaks jQuery .each
}
}
}
);
return isDirty;
}
let init_dirty = (ifStayFunc) => {
/* ifStayFunc: optional callback when user decides to stay on page
use .clearDirty class to avoid warning on some button, however:
if the button fires JavaScript do't use .clearDirty class and instead
use directly dirty(null) in code - to be sure it will run before window.location */
$('input, select').on('focusin', function(evt) {
if (!$('body').data('dirty_initialized')) {
dirty();
$('body').data('dirty_initialized', true);
}
});
window.addEventListener('beforeunload', (evt) => {
if (dirty(true)) {
if (ifStayFunc) {
ifStayFunc();
}
evt.preventDefault();
evt.returnValue = ''; // at least Google Chrome requires this
}
});
$('.clearDirty').on('click', function(evt) {
dirty(null);
});
};