检测未保存的更改
我需要在ASP .Net应用程序中实现“未保存的更改”提示。 如果用户修改了网页表单上的控件,并尝试在保存之前离开,则会出现一个提示,提示他们有未保存的更改,并为他们提供取消和保留当前页面的选项。 如果用户没有触及任何控件,则不应显示提示。
理想情况下,我想用JavaScript来实现这一点,但是在我开始编写自己的代码之前,是否有任何现有的框架或推荐的devise模式来实现这一目标? 理想情况下,我希望可以轻松地在多个页面上重复使用,而只需进行最less的更改。
使用jQuery:
var _isDirty = false; $("input[type='text']").change(function(){ _isDirty = true; }); // replicate for other input types and selects
根据需要与onunload
/ onbeforeunload
方法结合使用。
从注释中,以下引用所有input字段,而不重复代码:
$(':input').change(function () {
使用$(":input")
是指所有input,textarea,select和button元素。
一个难题是:
/** * 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
的事件注册。
在.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()">
并在代码隐藏,添加触发器的input字段以及重置提交/取消button….
btnSubmit.Attributes.Add("onclick", "isDirty = 0;"); btnCancel.Attributes.Add("onclick", "isDirty = 0;"); txtName.Attributes.Add("onchange", "setDirty();"); txtAddress.Attributes.Add("onchange", "setDirty();"); //etc..
以下使用浏览器的onbeforeunload函数和jQuery来捕获任何onchange事件。 IT还会查找任何提交或重置button来重置指示已发生更改的标志。
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;
感谢大家的回复。 我最终使用JQuery和Protect-Data插件实现了一个解决scheme。 这使我可以自动将监视应用于页面上的所有控件。
但是有一些注意事项,尤其是在处理ASP .Net应用程序时:
-
当用户select取消选项时,doPostBack函数将抛出JavaScript错误。 我不得不在doPostBack中的.submit周围进行try-catch来取消它。
-
在一些页面上,用户可以执行一个操作,执行回发到同一页面,但不是保存。 这导致了任何JavaScript逻辑重置,所以它认为什么东西可能有回发后没有改变。 我不得不实现一个隐藏的文本框,该页面被返回,并用于保存一个简单的布尔值,指示数据是否脏。 这得到持续回发。
-
您可能需要在页面上进行一些回发以不触发对话框,例如“保存”button。 在这种情况下,可以使用JQuery来添加一个将window.onbeforeunload设置为null的OnClick函数。
希望这对于那些需要类似实现的人来说是有帮助的。
以下解决scheme适用于原型(在FF,IE 6和Safari中testing)。 它使用了一个通用的窗体观察器(当窗体的任何字段被修改时,它将触发窗体:被更改),你也可以使用它来做其他的事情。
/* 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);
一般解决scheme在给定的页面支持多种forms( 只需复制并粘贴到您的项目 )
$(document).ready(function() { $('form :input').change(function() { $(this).closest('form').addClass('form-dirty'); }); $(window).bind('beforeunload', function() { if($('form:not(.ignore-changes).form-dirty').length > 0) { return 'You have unsaved changes, are you sure you want to discard them?'; } }); $('form').bind('submit',function() { $(this).closest('form').removeClass('form-dirty'); return true; }); });
注意:此解决scheme与其他解决scheme相结合,创build一个通用集成解决scheme。
特征:
- 只需复制并粘贴到您的应用程序。
- 支持多种forms。
- 因为他们的class级“forms肮脏”,你可以风格或使行动脏的forms。
- 您可以通过添加“ignore-changes”类来排除某些表单。
这是一个简单的JavaScript / jQuery解决scheme。 它由用户说明“撤销”,它被封装在一个function中以便于应用,并且在提交时不会失火。 只需调用该函数并传递表单的ID。
这个函数在页面加载时序列化表单一次,在用户离开页面之前再次序列化表单。 如果两个表单状态不同,则显示提示符。
试试看: http : //jsfiddle.net/skibulk/Ydt7Y/
function formUnloadPrompt(formSelector) { var formA = $(formSelector).serialize(), formB, formSubmit = false; // Detect Form Submit $(formSelector).submit( function(){ formSubmit = true; }); // Handle Form Unload window.onbeforeunload = function(){ if (formSubmit) return; formB = $(formSelector).serialize(); if (formA != formB) return "Your changes have not been saved."; }; } $(function(){ formUnloadPrompt('form'); });
我最近贡献了一个名为dirtyForms的开源jQuery插件。
该插件被devise用于dynamic添加HTML,支持多种forms,几乎可以支持任何对话框框架,在卸载对话框之前回退到浏览器,具有可插入的帮助框架以支持从定制编辑器获取脏状态(包含一个tinyMCE插件) ,在iFrames内工作,并且可以随意设置或重置脏状态。
使用jQuery检测表单变化非常简单:
var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed // check for form changes if ($('#formId').serialize() != formInitVal) { // show confirmation alert }
我扩展了上面的Slace的build议,包含了大多数可编辑的元素,并且还排除了某些元素(在这里用一个叫做“srSearch”的CSS样式)来引起脏标志的设置。
<script type="text/javascript"> var _isDirty = false; $(document).ready(function () { // Set exclude CSS class on radio-button list elements $('table.srSearch input:radio').addClass("srSearch"); $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () { _isDirty = true; }); }); $(window).bind('beforeunload', function () { if (_isDirty) { return 'You have unsaved changes.'; } });
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;
这正是创buildFleegix.js插件fleegix.form.diff
( http://js.fleegix.org/plugins/form/diff )的原因。 使用fleegix.form.toObject
( http://js.fleegix.org/ref#fleegix.form.toObject )序列化加载表单的初始状态,并将其保存在variables中,然后使用fleegix.form.diff
与当前状态进行fleegix.form.diff
在卸载fleegix.form.diff
。 易如反掌。
一种方法 ,使用数组来保存variables,以便跟踪变化。
这里有一个非常简单的方法来检测变化 ,但其余的不那么优雅。
另一种相当简单和小巧的方法,从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>
在IE中,document.ready将无法正常工作,它会更新input的值。
所以我们还需要在IE浏览器中处理document.ready函数内部绑定load事件。
下面是你应该放在document.ready函数中的代码。
$(document).ready(function () { $(window).bind("load", function () { $("input, select").change(function () {}); }); });