【问题标题】:Detecting Unsaved Changes检测未保存的更改
【发布时间】:2008-10-01 00:41:28
【问题描述】:

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

理想情况下,我想在 JavaScript 中实现这一点,但在我开始编写自己的代码之前,是否有任何现有的框架或推荐的设计模式来实现这一点?理想情况下,我想要一些可以轻松在多个页面中重复使用且更改最少的内容。

【问题讨论】:

  • 我也有同样的兴趣,只是对 asp.net 不感兴趣,但我认为那里有一个通用的解决方案。
  • 我在下面发布的解决方案可以在纯 HTML/Javscript 中使用。只需将“代码隐藏”更改为 HTML 标记本身的属性。
  • 我知道我在这里迟到了 5 年,但我认为我可以改进以前的解决方案......我刚刚在下面修复并更新了我的答案。

标签: javascript asp.net prompt


【解决方案1】:

使用 jQuery:

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

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

来自 cmets,以下引用所有输入字段,无需重复代码:

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

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

【讨论】:

  • 赞成,因为我使用了这个解决方案,但有一种更简单的方法。如果您使用: $(':input').change(function () { 那么它适用于所有输入字段,您不需要为其他输入类型复制。$(":input") 选择所有输入,textarea 、选择和按钮元素。
  • 我认为这在 Firefox 中可能存在问题。如果用户修改表单,然后单击浏览器的刷新按钮,页面将重新加载,Firefox 将使用用户以前的条目填充表单 - 而不触发更改事件。现在表单将变脏,但除非用户进行另一次编辑,否则不会设置标志。
  • 只是一个提示。如果表单是脏的,它仍然不意味着它包含真正改变的数据。为了更好的可用性,您必须将旧数据与新数据进行实际比较;)
  • 请记住,change for text-inputs 在离开/失去焦点时触发一次,而input 在每次击键时触发。 (我通常将它与 change 和 JQuery 的 one() 一起使用,以防止发生多个事件。)
【解决方案2】:

拼图的一部分:

/**
 * 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;
}

And another:

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 中我没有收到任何消息。我有一条默认消息“您确定要离开该页面吗?某些数据可能会丢失”(这或多或少是我的非英语浏览器向我显示的消息的翻译)
  • 这个脚本很棒,但对我来说,它在提交表单时也会显示警告消息。我通过在 onClick (onclick="window.onbeforeunload=null") 中解除绑定事件来解决此问题
  • 我喜欢比较 defaultValue 等属性而不是设置脏位的方法。这意味着如果有人更改了一个字段,然后又将其更改回来,那么表单将不会报告为脏。
  • 谢谢,太好了。你能告诉我们如何注册jquery吗?除非我在同一个 html 页面上有这个脚本,否则它不会触发。如果我将它放在外部 .js 文件中,则它不起作用。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    下面使用浏览器的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 重置不起作用,最初我选中它并再次取消选中,但警报被触发,当用户取消选中复选框时我如何重置
    【解决方案5】:

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

    但有一些注意事项,尤其是在处理 ASP .Net 应用程序时:

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

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

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

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

    【讨论】:

      【解决方案6】:

      在给定页面中支持多个表单的通用解决方案(只需复制并粘贴到您的项目中

      $(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”类来排除某些表单。

      【讨论】:

      • 这很好用,但我用 Aaron Powell 描述的 _isDirty 变量替换了添加的类。我没有看到需要在 HTML 中添加和删除类,而不是在 javascript 中添加和删除变量。
      • 这里使用 html 类的想法是为每个表单附加 'dirty' 属性,因为您不能简单地使用变量来制作通用的应用程序级 js/html 解决方案。
      • 换句话说,使用变量只适用于单个表单,否则您将需要一个脏标志数组,这将使解决方案变得更加复杂。
      • 感谢您解释这一点。我只需要一个表单页面。
      • 这太棒了!按原样工作。我推荐这个。唯一的问题是: beforeunload 返回不显示用户友好的消息(至少在大多数浏览器中) - 关于如何显示更用户友好的消息的任何建议?
      【解决方案7】:

      以下解决方案适用于原型(在 FF、IE 6 和 Safari 中测试)。它使用通用表单观察器(当表单的任何字段被修改时触发 form:changed),您也可以将其用于其他内容。

      /* 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);
      

      【讨论】:

        【解决方案8】:

        这是一个简单的 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');
        });
        

        【讨论】:

        • 我试过你的方法。它适用于 Chrome 和 Firefox,但不能使其适用于 Ipad Mini 上的 Safari ..
        【解决方案9】:

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

        该插件旨在处理动态添加的 HTML,支持多种表单,几乎可以支持任何对话框框架,在卸载对话框之前回退到浏览器,具有可插入的帮助框架以支持从自定义编辑器获取脏状态(一个 tinyMCE 插件包括在内),在 iFrames 内工作,脏状态可以随意设置或重置。

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

        【讨论】:

          【解决方案10】:

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

          var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed
          
          // check for form changes
          if ($('#formId').serialize() != formInitVal) {
              // show confirmation alert
          }
          

          【讨论】:

            【解决方案11】:

            我在上面 Slace 的建议上进行了扩展,包括大多数可编辑元素,并排除某些元素(此处使用称为“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.';
                        }
                    });        
            

            【讨论】:

              【解决方案12】:
                    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;

              【讨论】:

                【解决方案13】:

                这正是 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 与当前状态进行比较。很简单。

                【讨论】:

                  【解决方案14】:

                  很多过时的答案,所以这里有一些更现代的东西。

                  ES6

                  let dirty = false
                  document.querySelectorAll('form').forEach(e => e.onchange = () => dirty = true)
                  

                  【讨论】:

                    【解决方案15】:

                    One method,使用数组来保存变量,以便跟踪变化。

                    这是detect changes 的一个非常简单的方法,但其余的就没有那么优雅了。

                    另一种比较简单的小方法,来自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>
                    

                    【讨论】:

                      【解决方案16】:

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

                      所以我们需要在 document.ready 函数中绑定加载事件,该函数也将处理 IE 浏览器。

                      下面是你应该放在 document.ready 函数中的代码。

                       $(document).ready(function () {
                         $(window).bind("load", function () { 
                          $("input, select").change(function () {});
                         });
                      });
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-12-09
                        • 2013-01-28
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-04-09
                        • 1970-01-01
                        • 2012-03-18
                        相关资源
                        最近更新 更多