저장되지 않은 변경 사항 감지


94

ASP .Net 응용 프로그램에서 "저장되지 않은 변경 사항"프롬프트를 구현해야합니다. 사용자가 웹 양식에서 컨트롤을 수정하고 저장하기 전에 다른 곳으로 이동하려고하면 저장되지 않은 변경 사항이 있음을 경고하는 메시지가 나타나고 취소하고 현재 페이지에 머무를 수있는 옵션을 제공해야합니다. 사용자가 컨트롤을 터치하지 않은 경우 프롬프트가 표시되지 않아야합니다.

이상적으로는 이것을 JavaScript로 구현하고 싶지만 자체 코드를 롤링하기 전에이를 달성하기위한 기존 프레임 워크 또는 권장 디자인 패턴이 있습니까? 이상적으로는 최소한의 변경으로 여러 페이지에서 쉽게 재사용 할 수있는 것을 원합니다.


나는 asp.net이 아니라 같은 것에 관심이 있지만 거기에 일반적인 해결책이 있다고 생각합니다.
Michael Neale

아래에 게시 한 솔루션은 일반 HTML / Javscript에서 사용할 수 있습니다. "코드 숨김"을 HTML 태그 자체의 속성으로 변경하기 만하면됩니다.
Wayne

여기에 5 년 늦었다는 것을 알고 있지만 이전 솔루션을 개선 할 수있을 것 같습니다 ... 방금 아래 답변을 수정하고 업데이트했습니다.
skibulk

답변:


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
이 솔루션을 사용했기 때문에 Upvote하지만 약간 더 쉬운 방법이 있습니다. 사용하는 경우 : $ ( ': input'). change (function () {모든 입력 필드에 대해 작동하며 다른 입력 유형에 대해 복제 할 필요가 없습니다. $ ( ": input")은 모든 입력, 텍스트 영역을 선택합니다. 선택하고 버튼 요소.
CodeClimber

5
Firefox에서 문제가있을 수 있다고 생각합니다. 사용자가 양식을 수정 한 다음 브라우저의 새로 고침 버튼을 클릭하면 페이지가 다시로드되고 Firefox는 변경 이벤트를 발생시키지 않고 사용자의 이전 항목으로 양식을 채 웁니다. 이제 양식은 더럽지 만 사용자가 다른 편집을하지 않으면 플래그가 설정되지 않습니다.
Oskar Berggren 2014 년

8
힌트입니다. 양식이 더러워도 실제로 변경된 데이터가 포함되어 있다는 의미는 아닙니다. 실제로 새로운 오래된 데이터를 비교에보다 잘 사용할 수 있도록, 당신은)
슬라바 Fomin II

마음에 가지고 그 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에서는 메시지가 표시되지 않습니다. "페이지에서 나가시겠습니까? 일부 데이터가 손실 될 수 있습니다"라는 기본 메시지가 있습니다 (이는 영어가 아닌 브라우저에서 표시되는 메시지의 번역과
거의 비슷

1
이 스크립트는 굉장하지만 제 양식을 제출할 때 경고 메시지도 표시했습니다. onClick ( onclick="window.onbeforeunload=null") 에서 이벤트 바인딩을 해제하여이 문제를 해결했습니다.
Joost

1
더티 비트를 설정하는 것보다 defaultValue 등의 속성을 비교하는 방법을 좋아합니다. 즉, 누군가 필드를 변경 한 다음 다시 변경하면 양식이 더티로보고되지 않습니다.
thelem

감사합니다. 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;

Hi colin reset은 확인란을 배치했을 때 작동하지 않았습니다. 처음에는 확인했다가 다시 선택 취소했지만 경고가 발생합니다. 사용자가 확인란을 선택 취소하면 어떻게 재설정 할 수 있습니까?
Developer

8

모두 답장 해 주셔서 감사합니다. 결국 JQuery와 Protect-Data 플러그인을 사용하여 솔루션을 구현 했습니다. 이를 통해 페이지의 모든 컨트롤에 모니터링을 자동으로 적용 할 수 있습니다.

그러나 특히 ASP .Net 응용 프로그램을 다룰 때 몇 가지주의 사항이 있습니다.

  • 사용자가 취소 옵션을 선택하면 doPostBack 함수가 JavaScript 오류를 발생시킵니다. 그것을 억제하기 위해 doPostBack 내에서 .submit 호출 주위에 try-catch를 수동으로 넣어야했습니다.

  • 일부 페이지에서 사용자는 동일한 페이지에 대한 포스트 백을 수행하지만 저장이 아닌 작업을 수행 할 수 있습니다. 이로 인해 JavaScript 로직이 재설정되므로 포스트 백 후 변경된 사항이 없다고 생각합니다. 페이지와 함께 다시 게시되는 숨겨진 텍스트 상자를 구현해야했으며 데이터가 더러워 졌는지 여부를 나타내는 간단한 부울 값을 유지하는 데 사용됩니다. 이것은 포스트 백에서 지속됩니다.

  • 저장 버튼과 같은 페이지의 일부 포스트 백이 대화 상자를 트리거하지 않도록 할 수 있습니다. 이 경우 JQuery를 사용하여 window.onbeforeunload를 null로 설정하는 OnClick 함수를 추가 할 수 있습니다.

바라건대 이것은 비슷한 것을 구현해야하는 다른 사람에게 도움이되기를 바랍니다.


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에서 클래스를 추가하고 제거 할 필요가 없다고 생각했습니다.
Martin

1
여기서 html 클래스를 사용하는 아이디어는 일반 응용 프로그램 -wize js / html 솔루션을 만들기 위해 단순히 변수를 사용할 수 없기 때문에 각 양식에 'dirty'속성을 첨부하는 것입니다.
MhdSyrwan

1
즉, 변수를 사용하는 것은 단일 형식에서만 작동합니다. 그렇지 않으면 솔루션을 훨씬 더 복잡하게 만드는 더티 플래그 배열이 필요합니다.
MhdSyrwan

1
그 점을 설명 해주셔서 감사합니다. 내 필요는 단일 양식 페이지뿐이었습니다.
Martin

6

다음 솔루션은 프로토 타입에 대해 작동합니다 (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);

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에서는 작동하지만 Ipad Mini의 Safari에서는 작동하지 않습니다.
Dimitry K

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의 제안을 확장하여 대부분의 편집 가능한 요소를 포함하고 특정 요소 (여기서 "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.';
            }
        });        


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 = unloadPage;



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는 제대로 작동하지 않으며 입력 값을 업데이트합니다.

따라서 IE 브라우저에서도 처리 할 document.ready 함수 내에서 load 이벤트를 바인딩해야합니다.

아래는 document.ready 함수 안에 넣어야하는 코드입니다.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.