Phát hiện các thay đổi chưa được lưu


94

Tôi có yêu cầu triển khai lời nhắc "Thay đổi chưa lưu" trong ứng dụng ASP .Net. Nếu người dùng sửa đổi các điều khiển trên biểu mẫu web và cố gắng điều hướng đi trước khi lưu, lời nhắc sẽ xuất hiện cảnh báo họ rằng họ có các thay đổi chưa được lưu và cung cấp cho họ tùy chọn hủy và ở lại trang hiện tại. Lời nhắc sẽ không hiển thị nếu người dùng chưa chạm vào bất kỳ điều khiển nào.

Lý tưởng nhất là tôi muốn triển khai điều này trong JavaScript, nhưng trước khi tôi đi xuống con đường cuộn mã của riêng mình, có bất kỳ khuôn khổ hiện tại nào hoặc các mẫu thiết kế được đề xuất để đạt được điều này không? Lý tưởng nhất là tôi muốn thứ gì đó có thể dễ dàng được sử dụng lại trên nhiều trang với những thay đổi tối thiểu.


Tôi quan tâm đến điều tương tự, chỉ không phải cho asp.net, nhưng tôi nghĩ rằng có một giải pháp chung ngoài đó.
Michael Neale

Giải pháp tôi đã đăng dưới đây có thể được sử dụng trong HTML / Javscript thuần túy. Chỉ cần thay đổi "codebehind" thành các thuộc tính trong chính các thẻ HTML.
Wayne

Tôi biết mình đã trễ 5 năm ở đây, nhưng tôi nghĩ tôi có thể cải thiện các giải pháp trước đó ... Tôi chỉ sửa và cập nhật câu trả lời của mình bên dưới.
skibulk

Câu trả lời:


96

Sử dụng jQuery:

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

Kết hợp với onunload/ onbeforeunloadcác phương thức theo yêu cầu.

Từ các nhận xét, các tham chiếu sau tham chiếu đến tất cả các trường đầu vào, không trùng lặp mã:

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

Sử dụng $(":input")đề cập đến tất cả các phần tử đầu vào, vùng văn bản, lựa chọn và nút.


32
Hãy ủng hộ vì tôi đã sử dụng giải pháp này nhưng có một cách dễ dàng hơn một chút. Nếu bạn sử dụng: $ (': input'). Change (function () {thì nó hoạt động cho tất cả các trường đầu vào, bạn không cần phải sao chép cho các kiểu nhập khác. $ (": Input") chọn tất cả đầu vào, textarea , chọn và nút các phần tử.
CodeClimber

5
Tôi nghĩ rằng điều này có thể có vấn đề trong Firefox. Nếu người dùng sửa đổi biểu mẫu, sau đó nhấp vào nút Làm mới của trình duyệt, trang sẽ được tải lại và Firefox sẽ điền vào biểu mẫu với các mục nhập trước đó của người dùng - mà không kích hoạt sự kiện thay đổi. Bây giờ biểu mẫu sẽ bị bẩn, nhưng cờ sẽ không được đặt trừ khi người dùng thực hiện một chỉnh sửa khác.
Oskar Berggren

8
Chỉ là một gợi ý. Nếu biểu mẫu bị bẩn không có nghĩa là nó chứa dữ liệu thực sự đã thay đổi. Đối với khả năng sử dụng tốt hơn bạn phải thực sự so sánh dữ liệu cũ bằng cái mới;)
Slava Fomin II

Hãy nhớ rằng changeđối với nhập văn bản sẽ kích hoạt khi rời / mất tiêu điểm, trong khi inputkích hoạt cho mỗi lần nhấn phím. (Tôi thường sử dụng nó cùng changevới JQuery one(), nhằm ngăn chặn nhiều sự kiện.)
SpazzMarticus

46

Một phần của câu đố:

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

Và khác :

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.";
  }
};

Tóm lại tất cả, và bạn nhận được gì?

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.");

Có thể bạn cũng sẽ muốn thay đổi đăng ký của beforeunloadtrình xử lý sự kiện để sử dụng LIBRARY_OF_CHOICEđăng ký sự kiện của.


Trong Firefox 9.0.1, tôi không nhận được bất kỳ thông báo nào. Tôi có một tin nhắn mặc định nói "Bạn có chắc chắn bạn muốn rời khỏi trang Một số dữ liệu có thể bị mất?" (Đây là nhiều hay ít bản dịch của thông điệp hiển thị trình duyệt không phải tiếng Anh của tôi để tôi)
Romain Guidoux

1
Tập lệnh này thật tuyệt vời, nhưng đối với tôi, nó cũng hiển thị thông báo cảnh báo khi gửi biểu mẫu của tôi. Tôi đã sửa lỗi này bằng cách hủy liên kết sự kiện trong onClick ( onclick="window.onbeforeunload=null")
Joost

1
Tôi thích cách tiếp cận so sánh các thuộc tính defaultValue vv hơn là thiết lập một bit bẩn. Điều này có nghĩa là nếu ai đó thay đổi một trường, sau đó thay đổi lại nó, thì biểu mẫu sẽ không báo cáo là bẩn.
thelem

Cảm ơn, điều này thật tuyệt. Bạn có thể vui lòng cho chúng tôi biết cách đăng ký jquery không? Trừ khi tôi có tập lệnh này trên cùng một trang html, nó không kích hoạt. Nếu tôi có nó bên trong tệp .js bên ngoài, nó không hoạt động.
Shiva Naru

17

Trong trang .aspx, bạn cần một hàm Javascript để cho biết thông tin biểu mẫu có "bẩn" hay không

<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()">

và trong phần mã phía sau, thêm các trình kích hoạt vào các trường đầu vào cũng như đặt lại trên các nút gửi / hủy ....

btnSubmit.Attributes.Add("onclick", "isDirty = 0;");
btnCancel.Attributes.Add("onclick", "isDirty = 0;");
txtName.Attributes.Add("onchange", "setDirty();");
txtAddress.Attributes.Add("onchange", "setDirty();");
//etc..

9

Phần sau sử dụng hàm onbeforeunload của trình duyệt và jquery để nắm bắt bất kỳ sự kiện onchange nào. CNTT cũng tìm kiếm bất kỳ nút gửi hoặc đặt lại nào để đặt lại cờ cho biết các thay đổi đã xảy ra.

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 đã không làm việc khi tôi đặt một hộp kiểm tra, ban đầu tôi đã kiểm tra nó và không được kiểm soát một lần nữa nhưng cảnh báo được bị sa thải, làm thế nào tôi có thể thiết lập lại khi người dùng bỏ chọn hộp kiểm
Developer

8

Cảm ơn đã trả lời tất cả mọi người. Tôi đã kết thúc việc triển khai một giải pháp bằng cách sử dụng JQuery và plug-in Protect-Data . Điều này cho phép tôi tự động áp dụng giám sát cho tất cả các điều khiển trên một trang.

Tuy nhiên, có một số lưu ý, đặc biệt là khi xử lý ứng dụng ASP .Net:

  • Khi người dùng chọn tùy chọn hủy, hàm doPostBack sẽ tạo ra một lỗi JavaScript. Tôi đã phải thực hiện thủ công lệnh thử bắt xung quanh lệnh gọi .submit trong doPostBack để ngăn chặn nó.

  • Trên một số trang, người dùng có thể thực hiện một hành động thực hiện đăng lại cho cùng một trang, nhưng không phải là lưu. Điều này dẫn đến việc đặt lại bất kỳ logic JavaScript nào, vì vậy nó cho rằng không có gì thay đổi sau khi đăng lại khi có điều gì đó xảy ra. Tôi đã phải triển khai một hộp văn bản ẩn được đăng lại cùng với trang và được sử dụng để giữ một giá trị boolean đơn giản cho biết liệu dữ liệu có bị bẩn hay không. Điều này vẫn tồn tại qua các lần đăng lại.

  • Bạn có thể muốn một số bài đăng lại trên trang không kích hoạt hộp thoại, chẳng hạn như nút Lưu. Trong trường hợp này, bạn có thể sử dụng JQuery để thêm chức năng OnClick đặt window.onbeforeunload thành null.

Hy vọng rằng điều này sẽ hữu ích cho bất kỳ ai khác phải thực hiện một cái gì đó tương tự.


7

Giải pháp chung Hỗ trợ nhiều biểu mẫu trong một trang nhất định ( Chỉ cần sao chép và dán vào dự án của bạn )

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

Lưu ý: Giải pháp này được kết hợp từ các giải pháp của người khác để tạo ra một giải pháp tích hợp chung.

Đặc trưng:

  • Chỉ cần sao chép và dán vào ứng dụng của bạn.
  • Hỗ trợ nhiều biểu mẫu.
  • Bạn có thể tạo kiểu hoặc tạo các biểu mẫu hành động bẩn, vì chúng có lớp là "biểu mẫu bẩn".
  • Bạn có thể loại trừ một số biểu mẫu bằng cách thêm lớp 'bỏ qua thay đổi'.

Điều này hoạt động tốt, nhưng tôi thay thế các lớp đã thêm bằng _isDirtybiến như được mô tả bởi Aaron Powell. Tôi không thấy cần phải thêm và xóa các lớp khỏi HTML thay vì các biến trong javascript.
Martin

1
Ý tưởng của việc sử dụng các lớp html ở đây là đính kèm thuộc tính 'dirty' với mỗi biểu mẫu vì bạn không thể chỉ sử dụng các biến để tạo giải pháp js / html ứng dụng chung.
MhdSyrwan

1
Nói cách khác, việc sử dụng một biến sẽ chỉ hoạt động với một biểu mẫu duy nhất, nếu không, bạn sẽ cần một mảng cờ bẩn sẽ làm cho giải pháp phức tạp hơn nhiều.
MhdSyrwan

1
Cảm ơn vì đã giải thích điểm đó. Nhu cầu của tôi chỉ với một trang biểu mẫu duy nhất.
Martin

6

Giải pháp sau đây hoạt động cho nguyên mẫu (được thử nghiệm trong FF, IE 6 và Safari). Nó sử dụng trình quan sát biểu mẫu chung (biểu mẫu kích hoạt: được thay đổi khi bất kỳ trường nào của biểu mẫu đã được sửa đổi), bạn cũng có thể sử dụng trình quan sát này cho các nội dung khác.

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

Đây là một giải pháp javascript / jquery đơn giản. Nó chiếm quyền "hoàn tác" bởi người dùng, nó được đóng gói trong một hàm để dễ ứng dụng và nó không bắn nhầm khi gửi. Chỉ cần gọi hàm và chuyển ID của biểu mẫu của bạn.

Chức năng này tuần tự hóa biểu mẫu một lần khi trang được tải và một lần nữa trước khi người dùng rời khỏi trang. Nếu hai trạng thái biểu mẫu khác nhau, lời nhắc sẽ được hiển thị.

Hãy dùng thử: 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
Tôi đã thử cách tiếp cận của bạn. Nó hoạt động cho Chrome và Firefox, nhưng không thể làm cho nó hoạt động với Safari trên Ipad Mini ..
Dimitry K

4

Gần đây tôi đã đóng góp vào một plugin jQuery mã nguồn mở có tên là dirtyForms .

Plugin được thiết kế để hoạt động với HTML được thêm động, hỗ trợ nhiều biểu mẫu, có thể hỗ trợ hầu như bất kỳ khung hộp thoại nào, quay trở lại trình duyệt trước khi tải hộp thoại, có khung trợ giúp có thể cắm để hỗ trợ tình trạng bẩn từ trình chỉnh sửa tùy chỉnh (bao gồm một plugin tinyMCE) , hoạt động trong iFrames và tình trạng bẩn có thể được đặt hoặc đặt lại theo ý muốn.

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


4

Phát hiện các thay đổi biểu mẫu bằng cách sử dụng jQuery rất đơn giản:

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

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

2

Tôi đã mở rộng theo đề xuất của Slace ở trên, để bao gồm hầu hết các phần tử có thể chỉnh sửa và cũng loại trừ một số phần tử nhất định (với kiểu CSS gọi là "srSearch" ở đây) khỏi việc đặt cờ bẩn.

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



0

Một phương pháp , sử dụng mảng để giữ các biến để có thể theo dõi các thay đổi.

Đây là một phương pháp rất đơn giản để phát hiện các thay đổi , nhưng phần còn lại thì không hiệu quả bằng.

Một phương pháp khác khá đơn giản và nhỏ, từ 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

Trong IE document.ready sẽ không hoạt động bình thường, nó sẽ cập nhật các giá trị của đầu vào.

vì vậy chúng ta cần liên kết sự kiện tải bên trong hàm document.ready sẽ xử lý cho trình duyệt IE.

bên dưới là mã bạn nên đặt bên trong hàm document.ready.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.