Mendeteksi Perubahan yang Tidak Tersimpan

94

Saya memiliki persyaratan untuk mengimplementasikan prompt "Perubahan yang Tidak Tersimpan" dalam aplikasi ASP .Net. Jika pengguna mengubah kontrol di formulir web, dan mencoba keluar sebelum menyimpan, perintah akan muncul untuk memperingatkan mereka bahwa mereka memiliki perubahan yang belum disimpan, dan memberi mereka opsi untuk membatalkan dan tetap di halaman saat ini. Perintah seharusnya tidak ditampilkan jika pengguna belum menyentuh kontrol apa pun.

Idealnya saya ingin menerapkan ini dalam JavaScript, tetapi sebelum saya mulai menjalankan kode saya sendiri, apakah ada kerangka kerja yang ada atau pola desain yang disarankan untuk mencapai ini? Idealnya, saya menginginkan sesuatu yang dapat dengan mudah digunakan kembali di beberapa halaman dengan sedikit perubahan.

tbreffni.dll
sumber
Saya tertarik pada hal yang sama, hanya bukan untuk asp.net, tetapi saya pikir ada solusi umum di luar sana.
Michael Neale
Solusi yang saya posting di bawah ini dapat digunakan dalam HTML / Javscript biasa. Cukup ubah "codebehind" menjadi atribut di tag HTML itu sendiri.
Wayne
Saya tahu saya terlambat 5 tahun di sini, tetapi saya pikir saya dapat meningkatkan solusi sebelumnya ... Saya baru saja memperbaiki dan memperbarui jawaban saya di bawah ini.
skibulk

Jawaban:

96

Menggunakan jQuery:

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

Gabungkan dengan onunload/ onbeforeunloadmetode sesuai kebutuhan.

Dari komentar, berikut ini referensi semua bidang masukan, tanpa kode duplikat:

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

Menggunakan $(":input")mengacu pada semua elemen input, textarea, pilih, dan tombol.

Aaron Powell
sumber
32
Beri suara positif karena saya menggunakan solusi ini tetapi ada cara yang sedikit lebih mudah. Jika Anda menggunakan: $ (': input'). Change (function () {maka itu berfungsi untuk semua bidang input, Anda tidak perlu mereplikasi untuk jenis input lainnya. $ (": Input") memilih semua input, textarea , pilih dan elemen tombol.
CodeClimber
5
Saya rasa ini mungkin memiliki masalah di Firefox. Jika pengguna mengubah formulir, lalu mengklik tombol Refresh browser, halaman akan dimuat ulang dan Firefox akan mengisi formulir dengan entri sebelumnya pengguna - tanpa mengaktifkan perubahan-peristiwa. Sekarang formulir akan menjadi kotor, tetapi bendera tidak akan disetel kecuali pengguna melakukan pengeditan lain.
Oskar Berggren
8
Hanya petunjuk. Jika formulir kotor, itu tetap tidak berarti berisi data yang benar-benar berubah. Untuk kegunaan yang lebih baik, Anda harus membandingkan data lama dengan yang baru;)
Slava Fomin II
Ingatlah bahwa changeuntuk input teks memicu saat meninggalkan / kehilangan fokus, sedangkan inputmemicu untuk setiap penekanan tombol. (Saya biasanya menggunakannya bersama changedengan JQuery one(), untuk mencegah banyak peristiwa.)
SpazzMarticus
46

Salah satu bagian dari teka-teki:

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

Dan lainnya :

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

Selesaikan semuanya, dan apa yang Anda dapatkan?

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

Anda mungkin juga ingin mengubah registrasi beforeunloadevent handler untuk menggunakan LIBRARY_OF_CHOICEregistrasi event.

Jonny Buchanan
sumber
Di Firefox 9.0.1 saya tidak mendapatkan pesan apa pun. Saya memiliki pesan default yang mengatakan "Apakah Anda yakin ingin meninggalkan halaman? Beberapa data mungkin hilang" (Ini kurang lebih adalah terjemahan dari pesan yang ditampilkan browser non-Inggris saya kepada saya)
Romain Guidoux
1
Skrip ini luar biasa, tetapi bagi saya itu juga menampilkan pesan peringatan setelah mengirimkan formulir saya. Saya memperbaikinya dengan melepaskan acara di onClick ( onclick="window.onbeforeunload=null")
Joost
1
Saya suka pendekatan membandingkan properti defaultValue dll daripada menyetel sedikit kotor. Artinya jika seseorang mengubah bidang, lalu mengubahnya kembali, maka formulir tersebut tidak akan dilaporkan sebagai kotor.
thelem
Terima kasih, ini bagus. Bisakah Anda memberi tahu kami cara mendaftar jquery? Kecuali saya memiliki skrip ini di halaman html yang sama, skrip ini tidak akan menyala. Jika saya memilikinya di dalam file .js eksternal, itu tidak berfungsi.
Shiva Naru
17

Di halaman .aspx, Anda memerlukan fungsi Javascript untuk mengetahui apakah info formulir "kotor" atau tidak

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

dan di codebehind, tambahkan pemicu ke kolom input serta setel ulang pada tombol pengiriman / batal ....

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

Yang berikut ini menggunakan fungsi onbeforeunload browser dan jquery untuk merekam kejadian onchange. TI juga mencari tombol kirim atau setel ulang untuk menyetel ulang bendera yang menunjukkan perubahan telah terjadi.

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 Houghton
sumber
Hi colin reset tidak berfungsi ketika saya meletakkan kotak centang, awalnya saya mencentangnya dan tidak dicentang lagi tetapi peringatan dipecat, bagaimana saya bisa mengatur ulang ketika pengguna tidak mencentang kotak centang
Pengembang
8

Terima kasih atas balasannya semuanya. Saya akhirnya menerapkan solusi menggunakan JQuery dan plug-in Protect-Data . Ini memungkinkan saya untuk secara otomatis menerapkan pemantauan ke semua kontrol di halaman.

Namun ada beberapa peringatan, terutama saat berurusan dengan aplikasi ASP .Net:

  • Saat pengguna memilih opsi batal, fungsi doPostBack akan memunculkan kesalahan JavaScript. Saya harus secara manual melakukan coba-tangkap di sekitar panggilan .submit dalam doPostBack untuk menekannya.

  • Di beberapa halaman, pengguna dapat melakukan tindakan yang melakukan postback ke halaman yang sama, tetapi bukan penyimpanan. Hal ini menyebabkan logika JavaScript disetel ulang, jadi menurutnya tidak ada yang berubah setelah postback ketika ada sesuatu yang berubah. Saya harus menerapkan kotak teks tersembunyi yang diposting kembali dengan halaman, dan digunakan untuk menyimpan nilai boolean sederhana yang menunjukkan apakah datanya kotor. Ini akan tetap ada di seluruh pos balik.

  • Anda mungkin ingin beberapa postback pada halaman tidak memicu dialog, seperti tombol Simpan. Dalam kasus ini, Anda dapat menggunakan JQuery untuk menambahkan fungsi OnClick yang menyetel window.onbeforeunload ke null.

Semoga ini bermanfaat bagi siapa saja yang harus menerapkan sesuatu yang serupa.

tbreffni.dll
sumber
7

Solusi Umum Mendukung berbagai bentuk di halaman tertentu ( Cukup salin dan tempel di proyek Anda )

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

Catatan: Solusi ini digabungkan dari solusi lain untuk membuat solusi terintegrasi umum.

Fitur:

  • Cukup salin dan tempel ke aplikasi Anda.
  • Mendukung Berbagai Bentuk.
  • Anda dapat mengatur gaya atau membuat tindakan formulir kotor, karena kelas tersebut "bentuk-kotor".
  • Anda dapat mengecualikan beberapa formulir dengan menambahkan kelas 'abaikan-perubahan'.
MhdSyrwan
sumber
Ini bekerja dengan baik, tetapi saya mengganti kelas yang ditambahkan dengan _isDirtyvariabel seperti yang dijelaskan oleh Aaron Powell. Saya tidak melihat kebutuhan untuk menambahkan dan menghapus kelas dari HTML daripada variabel di javascript.
Martin
1
Ide menggunakan kelas html di sini adalah untuk melampirkan atribut 'dirty' pada setiap formulir karena Anda tidak bisa begitu saja menggunakan variabel untuk membuat solusi js / html application-wize umum.
MhdSyrwan
1
Dengan kata lain, menggunakan variabel hanya akan berfungsi untuk satu formulir, jika tidak, Anda akan memerlukan serangkaian tanda kotor yang akan membuat solusi menjadi jauh lebih rumit.
MhdSyrwan
1
Terima kasih telah menjelaskan hal itu. Kebutuhan saya hanya dengan satu halaman formulir.
Martin
6

Solusi berikut berfungsi untuk prototipe (diuji di FF, IE 6 dan Safari). Ini menggunakan pengamat bentuk umum (yang mengaktifkan formulir: diubah ketika ada bidang formulir telah dimodifikasi), yang dapat Anda gunakan untuk hal lain juga.

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

Berikut solusi javascript / jquery yang sederhana. Ini menyumbang "undos" oleh pengguna, itu dikemas dalam fungsi untuk kemudahan aplikasi, dan tidak salah kirim saat dikirim. Panggil saja fungsinya dan berikan ID formulir Anda.

Fungsi ini membuat serial formulir sekali saat halaman dimuat, dan sekali lagi sebelum pengguna meninggalkan halaman. Jika dua status formulir berbeda, prompt ditampilkan.

Cobalah: 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');
});
skibulk
sumber
2
Saya mencoba pendekatan Anda. Ini berfungsi untuk Chrome dan Firefox, tetapi tidak dapat membuatnya berfungsi untuk Safari di Ipad Mini ..
Dimitry K
4

Saya baru-baru ini berkontribusi pada plugin jQuery open source yang disebut dirtyForms .

Plugin ini dirancang untuk bekerja dengan HTML yang ditambahkan secara dinamis, mendukung berbagai bentuk, dapat mendukung hampir semua kerangka dialog, kembali ke browser sebelum membuka dialog, memiliki kerangka kerja pembantu yang dapat dicolok untuk mendukung mendapatkan status kotor dari editor khusus (termasuk plugin tinyMCE) , berfungsi dalam iFrames, dan status kotor dapat disetel atau disetel ulang sesuka hati.

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

NightOwl888
sumber
4

Mendeteksi perubahan bentuk dengan menggunakan jQuery sangat sederhana:

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

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

Saya memperluas saran Slace di atas, untuk menyertakan sebagian besar elemen yang dapat diedit dan juga mengecualikan elemen tertentu (dengan gaya CSS yang disebut "srSearch" di sini) dari penyetelan tanda kotor.

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

PeterX
sumber
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;

Ankit
sumber
0

Salah satu metode , menggunakan array untuk menampung variabel sehingga perubahan dapat dilacak.

Berikut adalah metode yang sangat sederhana untuk mendeteksi perubahan , tetapi yang lainnya tidak begitu elegan.

Metode lain yang cukup sederhana dan kecil, dari 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>
Adam Davis
sumber
0

Dalam dokumen IE. Sudah tidak akan berfungsi dengan baik itu akan memperbarui nilai input.

jadi kita perlu mengikat event load di dalam fungsi document.ready yang akan menangani browser IE juga.

di bawah ini adalah kode yang harus Anda masukkan ke dalam fungsi document.ready.

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