检测未保存的更改
我需要在ASP .Net应用程序中实现"未保存的更改"提示。如果用户修改了Web表单上的控件,并尝试在保存之前导航离开,则将出现提示,警告他们尚未保存更改,并为他们提供取消并保留在当前页面上的选项。如果用户未触摸任何控件,则不应显示该提示。
理想情况下,我想用JavaScript实现此功能,但是在我开始滚动自己的代码之前,是否有任何现有的框架或者推荐的设计模式来实现这一目标?理想情况下,我希望可以以最小的更改轻松地在多个页面之间重用这些内容。
解决方案
在.aspx页中,我们需要一个Javascript函数来判断表单信息是否为"脏"
<script language="javascript"> var isDirty = false; function setDirty() { isDirty = true; } function checkSave() { var sSave; if (isDirty == true) { sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving."); if (sSave == true) { document.getElementById('__EVENTTARGET').value = 'btnSubmit'; document.getElementById('__EVENTARGUMENT').value = 'Click'; window.document.formName.submit(); } else { return true; } } } </script> <body class="StandardBody" onunload="checkSave()">
并在后面的代码中,将触发器添加到输入字段,以及在提交/取消按钮上进行重置...。
btnSubmit.Attributes.Add("onclick", "isDirty = 0;"); btnCancel.Attributes.Add("onclick", "isDirty = 0;"); txtName.Attributes.Add("onchange", "setDirty();"); txtAddress.Attributes.Add("onchange", "setDirty();"); //etc..
一种方法,使用数组保存变量,以便可以跟踪更改。
这是一种检测更改的非常简单的方法,但其余方法却不那么优雅。
Farfetched博客提供的另一种相当简单且很小的方法:
<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()"> <form> <select name=a multiple> <option value=1>1 <option value=2>2 <option value=3>3 </select> <input name=b value=123> <input type=submit> </form> <script> var changed = 0; function recordChange() { changed = 1; } function recordChangeIfChangeKey(myevent) { if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey) recordChange(myevent); } function ignoreChange() { changed = 0; } function lookForChanges() { var origfunc; for (i = 0; i < document.forms.length; i++) { for (j = 0; j < document.forms[i].elements.length; j++) { var formField=document.forms[i].elements[j]; var formFieldType=formField.type.toLowerCase(); if (formFieldType == 'checkbox' || formFieldType == 'radio') { addHandler(formField, 'click', recordChange); } else if (formFieldType == 'text' || formFieldType == 'textarea') { if (formField.attachEvent) { addHandler(formField, 'keypress', recordChange); } else { addHandler(formField, 'keypress', recordChangeIfChangeKey); } } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') { addHandler(formField, 'change', recordChange); } } addHandler(document.forms[i], 'submit', ignoreChange); } } function warnOfUnsavedChanges() { if (changed) { if ("event" in window) //ie event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.'; else //netscape return false; } } function addHandler(target, eventName, handler) { if (target.attachEvent) { target.attachEvent('on'+eventName, handler); } else { target.addEventListener(eventName, handler, false); } } </script>
难题之一:
/** * Determines if a form is dirty by comparing the current value of each element * with its default value. * * @param {Form} form the form to be checked. * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code> * otherwise. */ function formIsDirty(form) { for (var i = 0; i < form.elements.length; i++) { var element = form.elements[i]; var type = element.type; if (type == "checkbox" || type == "radio") { if (element.checked != element.defaultChecked) { return true; } } else if (type == "hidden" || type == "password" || type == "text" || type == "textarea") { if (element.value != element.defaultValue) { return true; } } else if (type == "select-one" || type == "select-multiple") { for (var j = 0; j < element.options.length; j++) { if (element.options[j].selected != element.options[j].defaultSelected) { return true; } } } } return false; }
还有一个:
window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms["someForm"])) { // For IE and Firefox if (e) { e.returnValue = "You have unsaved changes."; } // For Safari return "You have unsaved changes."; } };
全部包起来,我们会得到什么?
var confirmExitIfModified = (function() { function formIsDirty(form) { // ...as above } return function(form, message) { window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms[form])) { // For IE and Firefox if (e) { e.returnValue = message; } // For Safari return message; } }; }; })(); confirmExitIfModified("someForm", "You have unsaved changes.");
我们可能还希望将beforeunload
事件处理程序的注册更改为使用`LIBRARY_OF_CHOICE'的事件注册。
使用jQuery:
var _isDirty = false; $("input[type='text']").change(function(){ _isDirty = true; }); // replicate for other input types and selects
根据需要与onunload / onbeforeunload方法结合使用。
从注释中,以下内容引用了所有输入字段,而没有重复的代码:
$(':input').change(function () {
使用$(":input")
引用所有输入,文本区域,选择和按钮元素。
感谢大家的答复。我最终使用JQuery和Protect-Data插件实现了一个解决方案。这使我可以自动将监视应用于页面上的所有控件。
但是,有一些警告,尤其是在处理ASP .Net应用程序时:
- 当用户选择取消选项时,doPostBack函数将引发JavaScript错误。我不得不在doPostBack内的.submit调用周围手动放置try-catch来抑制它。
- 在某些页面上,用户可以执行一个操作,该操作执行回发到同一页面的操作,但不是保存操作。这会导致所有JavaScript逻辑重置,因此它认为回发后可能没有任何变化。我必须实现一个隐藏的文本框,该文本框将与页面一起发布回去,并用于保存一个简单的布尔值,该值指示数据是否脏了。这会在回发之间持续存在。
- 我们可能希望页面上的某些回发操作不会触发对话框,例如"保存"按钮。在这种情况下,可以使用JQuery添加一个OnClick函数,该函数会将window.onbeforeunload设置为null。
希望这对必须实现类似功能的其他人有所帮助。
这正是Fleegix.js插件fleegix.form.diff
(http://js.fleegix.org/plugins/form/diff)创建的目的。使用fleegix.form.toObject
(http://js.fleegix.org/ref#fleegix.form.toObject)序列化加载时表单的初始状态,并将其保存在变量中,然后使用fleegix.form.diff
在卸载。非常简单。
以下解决方案适用于原型(已在FF,IE 6和Safari中测试)。它使用通用的表单观察器(触发表单:在修改表单的任何字段时更改表单),我们也可以将其用于其他内容。
/* use this function to announce changes from your own scripts/event handlers. * Example: onClick="makeDirty($(this).up('form'));" */ function makeDirty(form) { form.fire("form:changed"); } function handleChange(form, event) { makeDirty(form); } /* generic form observer, ensure that form:changed is being fired whenever * a field is being changed in that particular for */ function setupFormChangeObserver(form) { var handler = handleChange.curry(form); form.getElements().each(function (element) { element.observe("change", handler); }); } /* installs a form protector to a form marked with class 'protectForm' */ function setupProtectForm() { var form = $$("form.protectForm").first(); /* abort if no form */ if (!form) return; setupFormChangeObserver(form); var dirty = false; form.observe("form:changed", function(event) { dirty = true; }); /* submitting the form makes the form clean again */ form.observe("submit", function(event) { dirty = false; }); /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */ window.onbeforeunload = function(event) { if (dirty) { return "There are unsaved changes, they will be lost if you leave now."; } }; } document.observe("dom:loaded", setupProtectForm);
以下使用浏览器的onbeforeunload函数和jquery捕获任何onchange事件。 IT部门还会寻找任何提交或者重置按钮来重置标志,以指示发生了更改。
dataChanged = 0; // global variable flags unsaved changes function bindForChange(){ $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1}) $(':reset,:submit').bind('click',function(event) { dataChanged = 0 }) } function askConfirm(){ if (dataChanged){ return "You have some unsaved changes. Press OK to continue without saving." } } window.onbeforeunload = askConfirm; window.onload = bindForChange;