การตรวจจับการเปลี่ยนแปลงที่ไม่ได้บันทึก


95

ฉันมีความต้องการที่จะใช้พรอมต์ "Unsaved Changes" ในแอปพลิเคชัน ASP .Net หากผู้ใช้แก้ไขการควบคุมบนฟอร์มบนเว็บและพยายามที่จะออกไปก่อนที่จะบันทึกข้อความแจ้งควรปรากฏขึ้นเพื่อเตือนว่าพวกเขามีการเปลี่ยนแปลงที่ยังไม่ได้บันทึกและให้ตัวเลือกในการยกเลิกและอยู่ในหน้าปัจจุบัน ข้อความแจ้งไม่ควรปรากฏขึ้นหากผู้ใช้ไม่ได้แตะปุ่มควบคุมใด ๆ

ตามหลักการแล้วฉันต้องการใช้สิ่งนี้ใน JavaScript แต่ก่อนที่ฉันจะลงไปสู่เส้นทางการรีดโค้ดของตัวเองมีเฟรมเวิร์กที่มีอยู่หรือรูปแบบการออกแบบที่แนะนำเพื่อให้บรรลุสิ่งนี้หรือไม่ ตามหลักการแล้วฉันต้องการบางสิ่งที่สามารถนำมาใช้ซ้ำในหลาย ๆ หน้าได้โดยมีการเปลี่ยนแปลงน้อย


ฉันสนใจสิ่งเดียวกันไม่ใช่สำหรับ asp.net แต่ฉันคิดว่ามีวิธีแก้ปัญหาทั่วไปอยู่ที่นั่น
Michael Neale

วิธีการแก้ปัญหาที่ฉันโพสต์ด้านล่างสามารถใช้ได้ใน HTML / Javscript ธรรมดา เพียงแค่เปลี่ยน "codebehind" เป็นแอตทริบิวต์ในแท็ก 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
โหวตขึ้นเพราะฉันใช้วิธีนี้ แต่มีวิธีที่ง่ายกว่าเล็กน้อย หากคุณใช้: $ (': input'). change (function () {ซึ่งใช้ได้กับช่องป้อนข้อมูลทั้งหมดคุณไม่จำเป็นต้องทำซ้ำสำหรับประเภทอินพุตอื่น $ (": input") จะเลือกอินพุตทั้งหมด, textarea เลือกและปุ่มองค์ประกอบ
CodeClimber

5
ฉันคิดว่าสิ่งนี้อาจมีปัญหาใน Firefox หากผู้ใช้แก้ไขแบบฟอร์มจากนั้นคลิกปุ่มรีเฟรชเบราว์เซอร์หน้านั้นจะโหลดซ้ำและ Firefox จะเติมข้อมูลในแบบฟอร์มด้วยรายการก่อนหน้าของผู้ใช้โดยไม่เริ่มเหตุการณ์การเปลี่ยนแปลง ตอนนี้แบบฟอร์มจะสกปรก แต่จะไม่มีการตั้งค่าสถานะเว้นแต่ผู้ใช้จะทำการแก้ไขอีกครั้ง
Oskar Berggren

8
เพียงแค่คำใบ้ หากฟอร์มสกปรกก็ยังไม่ได้หมายความว่ามีข้อมูลที่เปลี่ยนแปลงจริงๆ เพื่อการใช้งานที่ดีขึ้นคุณต้องเปรียบเทียบข้อมูลเก่ากับข้อมูลใหม่จริงๆ;)
Slava Fomin II

โปรดทราบว่าchangeสำหรับอินพุตข้อความจะทริกเกอร์หนึ่งครั้งเมื่อออกจาก / สูญเสียโฟกัสในขณะที่inputทริกเกอร์สำหรับการกดแป้นพิมพ์ทุกครั้ง (โดยปกติฉันจะใช้ร่วมchangeกับ JQuery 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

1
สคริปต์นี้ยอดเยี่ยม แต่สำหรับฉันมันยังแสดงข้อความเตือนเมื่อส่งแบบฟอร์มของฉัน ฉันแก้ไขสิ่งนี้โดยการยกเลิกการผูกเหตุการณ์ใน onClick ( onclick="window.onbeforeunload=null")
Joost

1
ฉันชอบวิธีเปรียบเทียบคุณสมบัติ defaultValue etc มากกว่าการตั้งค่าบิตสกปรก ซึ่งหมายความว่าหากมีผู้เปลี่ยนฟิลด์จากนั้นให้เปลี่ยนกลับฟอร์มจะไม่รายงานว่าสกปรก
thelem

ขอบคุณมันเยี่ยมมาก คุณช่วยบอกวิธีการลงทะเบียน jquery ได้ไหม? เว้นแต่ฉันจะมีสคริปต์นี้ในหน้า html เดียวกันสคริปต์จะไม่เริ่มทำงาน ถ้าฉันมีมันอยู่ในไฟล์. js ภายนอกมันจะไม่ทำงาน
Shiva Naru

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

และใน codebehind ให้เพิ่มทริกเกอร์ลงในช่องป้อนข้อมูลรวมทั้งรีเซ็ตปุ่มส่ง / ยกเลิก ...

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 นอกจากนี้ไอทียังมองหาปุ่มส่งหรือรีเซ็ตเพื่อรีเซ็ตค่าสถานะที่ระบุว่ามีการเปลี่ยนแปลงเกิดขึ้น

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;

สวัสดีการรีเซ็ตโคลินไม่ทำงานเมื่อฉันวางช่องทำเครื่องหมายในตอนแรกฉันได้ตรวจสอบและยกเลิกการเลือกอีกครั้ง แต่การแจ้งเตือนกำลังเริ่มทำงานฉันจะรีเซ็ตได้อย่างไรเมื่อผู้ใช้ไม่เลือกช่องทำเครื่องหมาย
นักพัฒนา

8

ขอบคุณสำหรับการตอบกลับทุกคน ฉันลงเอยด้วยการใช้โซลูชันโดยใช้ JQuery และปลั๊กอินProtect-Data สิ่งนี้ทำให้ฉันใช้การตรวจสอบกับการควบคุมทั้งหมดบนเพจโดยอัตโนมัติ

อย่างไรก็ตามมีข้อแม้บางประการโดยเฉพาะเมื่อจัดการกับแอปพลิเคชัน ASP .Net:

  • เมื่อผู้ใช้เลือกตัวเลือกยกเลิกฟังก์ชัน doPostBack จะแสดงข้อผิดพลาดของ JavaScript ฉันต้องทำการลองจับด้วยตนเองรอบ ๆ . ส่งการโทรภายใน doPostBack เพื่อระงับมัน

  • ในบางเพจผู้ใช้สามารถดำเนินการที่ดำเนินการโพสต์แบ็คไปยังเพจเดียวกัน แต่ไม่สามารถบันทึกได้ สิ่งนี้ส่งผลให้เกิดการรีเซ็ตลอจิก 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;
    });
});

หมายเหตุ: โซลูชันนี้รวมจากโซลูชันของผู้อื่นเพื่อสร้างโซลูชันแบบรวมทั่วไป

คุณสมบัติ:

  • เพียงคัดลอกและวางลงในแอปของคุณ
  • รองรับหลายรูปแบบ
  • คุณสามารถจัดรูปแบบหรือสร้างรูปแบบการกระทำที่สกปรกได้เนื่องจากเป็นคลาสที่ "ฟอร์มสกปรก"
  • คุณสามารถยกเว้นบางรูปแบบได้โดยเพิ่มคลาส 'ละเว้นการเปลี่ยนแปลง'

สิ่งนี้ใช้ได้ดี แต่ฉันแทนที่คลาสที่เพิ่มด้วย_isDirtyตัวแปรตามที่ Aaron Powell อธิบายไว้ ฉันไม่เห็นความจำเป็นในการเพิ่มและลบคลาสออกจาก HTML แทนที่จะเป็นตัวแปรในจาวาสคริปต์
Martin

1
แนวคิดในการใช้คลาส html ที่นี่คือการแนบแอตทริบิวต์ 'สกปรก' กับแต่ละรูปแบบเนื่องจากคุณไม่สามารถใช้ตัวแปรเพื่อสร้างโซลูชัน js / html ของแอปพลิเคชันทั่วไปได้
MhdSyrwan

1
กล่าวอีกนัยหนึ่งคือการใช้ตัวแปรจะใช้ได้กับรูปแบบเดียวเท่านั้นมิฉะนั้นคุณจะต้องใช้แฟล็กสกปรกซึ่งจะทำให้การแก้ปัญหาซับซ้อนขึ้นมาก
MhdSyrwan

1
ขอบคุณที่อธิบายประเด็นนั้น ความต้องการของฉันมีเพียงหน้าแบบฟอร์มเดียว
Martin

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 Mini ได้ ..
Dimitry K

4

ฉันเพิ่งสนับสนุนปลั๊กอิน jQuery แบบโอเพนซอร์สชื่อdirtyForms dirtyForms

ปลั๊กอินได้รับการออกแบบมาเพื่อทำงานร่วมกับ HTML ที่เพิ่มแบบไดนามิกรองรับหลายรูปแบบสามารถรองรับกรอบโต้ตอบแทบทุกรูปแบบกลับไปที่เบราว์เซอร์ก่อนที่จะยกเลิกการโต้ตอบมีกรอบตัวช่วยที่เสียบได้เพื่อรองรับการรับสถานะสกปรกจากโปรแกรมแก้ไขที่กำหนดเอง (รวมปลั๊กอิน tinyMCE) ทำงานภายใน iFrames และสถานะสกปรกสามารถตั้งค่าหรือรีเซ็ตได้ตามต้องการ

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


1

นี่คือสิ่งที่ปลั๊กอิน 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เมื่อยกเลิกการโหลด ง่ายเหมือนพาย


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

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

0

คำตอบที่ล้าสมัยจำนวนมากดังนั้นนี่คือสิ่งที่ทันสมัยกว่าเล็กน้อย

ES6

let dirty = false
document.querySelectorAll('form').forEach(e => e.onchange = () => dirty = true)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.