我的申请表里有几页表格。
如何确保表单的安全性,以便在用户导航离开或关闭浏览器选项卡时,提示他们确认是否确实希望保留未保存数据的表单?
我的申请表里有几页表格。
如何确保表单的安全性,以便在用户导航离开或关闭浏览器选项卡时,提示他们确认是否确实希望保留未保存数据的表单?
当前回答
建在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上进行了测试。
其他回答
测试了伊莱·格雷的通用解决方案,只有在我把代码简化到
'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;
}
});
})();
对his的修改删除了target[defaultValue]的使用,只使用target。dataset[defaultValue]来存储真实的默认值。
我添加了一个'saved'事件监听器,'saved'事件将由您自己在保存操作成功时触发。
但是这个“通用”的解决方案只适用于浏览器,而不适用于应用程序的webview,例如微信浏览器。
为了使它在微信浏览器(部分)也工作,另一个改进再次:
'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");
}
})();
通过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.
});
你可以谷歌JQuery表单序列化函数,这将收集所有表单输入并保存在数组中。我想这个解释就足够了:)
我做了下面的代码。它可以比较所有字段的变化(除了那些用. ignoredirty类标记的字段),也可以只比较当前可见的字段。它可以为Javascript添加的新字段重新初始化。因此,我保存的不是表单状态,而是每个控件的状态。
/* 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;
}
我有额外的Chrome自动填充值的麻烦,因为它很难初始化和已经加载它们。所以我不初始化页面加载,但在任何focusin事件。(但是:也许JavaScript更改控件值仍然存在问题。)我使用下面的代码,我在页面加载调用:
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);
});
};
因此,我将. cleardirty类添加到提供Save的按钮中,这样就可以防止在这种情况下出现警告。 回调ifStayFunc允许我做一些事情,如果用户将停留在页面上,而他是警告。通常我可以显示额外的保存按钮(如果我仍然只看到一些默认/主按钮,这使得安全+SomethingMore -我想允许保存没有这个“SomethingMore”)。
建在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上进行了测试。
我做了不同的,分享在这里,以便有人可以得到帮助,只测试Chrome。
我想警告用户关闭标签只有当有一些变化。
<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.";
}
};
工作很好,但有一个其他的问题,当我提交的形式,我得到不想要的警告,我看到了很多解决方法,这是因为onbeforeunload火灾之前onsubmit,这就是为什么我们不能处理它在onsubmit事件像onbeforeunload = null,但onclick事件提交按钮火灾前这两个事件,所以我更新了代码
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;
});