检测未保存的更改


94

我需要在ASP .Net应用程序中实现“未保存的更改”提示。如果用户修改了Web表单上的控件,并试图在保存之前导航离开,则将出现提示,警告他们尚未保存更改,并为他们提供取消并保留在当前页面上的选项。如果用户未触摸任何控件,则不应显示该提示。

理想情况下,我想用JavaScript实现此功能,但是在我开始滚动自己的代码之前,是否有任何现有的框架或推荐的设计模式来实现这一目标?理想情况下,我希望可以以最小的更改轻松地在多个页面上重复使用的东西。


我对此感兴趣,只是对asp.net不感兴趣,但是我认为这里有一个通用的解决方案。
Michael Neale

我在下面发布的解决方案可以在纯HTML / Javscript中使用。只需将“代码隐藏”更改为HTML标记本身中的属性。
韦恩

我知道我在这里迟到了5年,但我认为我可以改善以前的解决方案...我只是在下面固定并更新了我的答案。
13年

Answers:


96

使用jQuery:

var _isDirty = false;
$("input[type='text']").change(function(){
  _isDirty = true;
});
// replicate for other input types and selects

根据需要与onunload/ onbeforeunload方法结合。

从注释中,以下内容引用了所有输入字段,而没有重复的代码:

$(':input').change(function () {

使用$(":input")是指所有输入,文本区域,选择和按钮元素。


32
支持,因为我使用了此解决方案,但是有一种更简单的方法。如果您使用:$(':input')。change(function(){然后适用于所有输入字段,则无需为其他输入类型进行复制。$(“:input”)选择所有输入,textarea ,选择和按钮元素
。– CodeClimber

5
我认为这可能在Firefox中有问题。如果用户修改了表单,则单击浏览器的“刷新”按钮,将重新加载页面,并且Firefox将使用用户先前的条目填充表单-而不会触发更改事件。现在,表单将变脏,但是除非用户再次进行编辑,否则不会设置该标志。
Oskar Berggren 2014年

8
只是一个提示。如果表单很脏,则仍然并不意味着它包含真正更改的数据。为了更好的可用性,您必须将旧数据与新数据进行实际比较;)
Slava Fomin II 2014年

请记住,change对于文本输入,一旦离开/失去焦点就会触发一次,而input每次击键都会触发一次。(我通常将它与changeJQuery 一起使用one(),以防止发生多个事件。)
SpazzMarticus

46

难题之一:

/**
 * 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的事件注册。


在Firefox 9.0.1中,我没有收到任何消息。我有一条默认消息“您确定要离开页面吗?某些数据可能会丢失”(这或多或少是我的非英语浏览器显示给我的消息的翻译)
Romain Guidoux 2011年

1
这个脚本很棒,但是对我来说,它在提交我的表单时也显示了警告消息。我固定这通过在一个onClick(解除绑定事件onclick="window.onbeforeunload=null"
Joost的

1
我喜欢比较defaultValue等属性的方法,而不是设置肮脏的位。这意味着,如果有人更改了一个字段,然后又将其更改了,那么该表单将不会报告为脏页。
thelem 2014年

谢谢,太好了。您能告诉我们如何注册jquery吗?除非我在同一HTML页面上具有此脚本,否则它不会触发。如果我将其放在外部.js文件中,则无法正常工作。
希瓦·纳鲁

17

在.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..

9

以下使用浏览器的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;

嗨,当我放置一个复选框时,colin重置不起作用,最初我选中了它,然后再次取消选中,但是警报被激发了,当用户取消选中该复选框时,我该如何重置
开发人员

8

感谢大家的答复。我最终使用JQuery和Protect-Data插件实现了一个解决方案。这使我可以自动将监视应用于页面上的所有控件。

但是,有一些警告,尤其是在处理ASP .Net应用程序时:

  • 当用户选择取消选项时,doPostBack函数将引发JavaScript错误。我不得不在doPostBack内的.submit调用周围手动放置try-catch来抑制它。

  • 在某些页面上,用户可以执行一个操作,该操作执行回发到同一页面,但不是保存。这会导致所有JavaScript逻辑重置,因此它认为回发后可能没有任何变化。我必须实现一个隐藏的文本框,该文本框将与页面一起发布,并用于保存一个简单的布尔值,该值指示数据是否脏了。这会在回发之间持续存在。

  • 您可能希望页面上的某些回发操作不会触发对话框,例如“保存”按钮。在这种情况下,可以使用JQuery添加一个OnClick函数,该函数会将window.onbeforeunload设置为null。

希望这对必须实现类似功能的其他人有所帮助。


7

常规解决方案在给定页面中支持多种表单(只需将其复制并粘贴到您的项目中

$(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;
    });
});

注意:此解决方案与其他解决方案相结合,以创建通用的集成解决方案。

特征:

  • 只需复制并粘贴到您的应用中即可。
  • 支持多种形式。
  • 您可以设置行为样式或使其成为肮脏的表单,因为它们具有“ form-dirty”类。
  • 您可以通过添加类“ ignore-changes”来排除某些形式。

效果很好,但是我用_isDirtyAaron Powell所述的变量替换了添加的类。我没有看到需要从HTML中添加和删除类,而不是从javascript中的变量中删除类。
马丁

1
在这里使用html类的想法是在每个表单上附加'dirty'属性,因为您不能简单地使用变量来创建通用的application-wize js / html解决方案。
MhdSyrwan '16

1
换句话说,使用变量仅适用于单一形式,否则您将需要一组脏标志,这将使解决方案更加复杂。
MhdSyrwan '16

1
感谢您解释这一点。我只需要一个表单页面。
马丁

6

以下解决方案适用于原型(已在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);

6

这是一个简单的javascript / jquery解决方案。它解决了用户的“撤消”,将其封装在一个函数中以简化应用程序,并且在提交时不会触发错误。只需调用该函数并传递表单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');
});

2
我尝试了您的方法。它适用于Chrome和Firefox,但不能让它的Safari浏览器在iPad迷你工作..
德米特里ķ

4

我最近为一个名为dirtyForms的开源jQuery插件做出了贡献。

该插件旨在与动态添加的HTML一起使用,支持多种形式,几乎可以支持任何对话框框架,在卸载对话框之前回退到浏览器,具有可插入的帮助程序框架,以支持从自定义编辑器获取脏状态(包括tinyMCE插件)可以在iFrame中使用,脏状态可以随意设置或重置。

https://github.com/snikch/jquery.dirtyforms


4

使用jQuery检测表单更改非常简单:

var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed

// check for form changes
if ($('#formId').serialize() != formInitVal) {
    // show confirmation alert
}

2

我在上面对Slace的建议进行了扩展,包括了大多数可编辑元素,还排除了某些元素(此处使用CSS样式称为“ srSearch”的样式)导致设置脏标志。

<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.';
            }
        });        


2
      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 =卸载页面;



0

一种方法,使用数组保存变量,以便可以跟踪更改。

这是一种检测更改的非常简单的方法,但其余方法却不那么优雅。

Farfetched Blog中的另一种方法相当简单而且很小:

<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>

0

在IE document.ready中无法正常工作,它将更新输入值。

因此,我们需要将load事件绑定到document.ready函数中,该函数也将为IE浏览器处理。

以下是您应该放入document.ready函数中的代码。

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.