เตือนผู้ใช้ก่อนออกจากหน้าเว็บที่มีการเปลี่ยนแปลงที่ไม่ได้บันทึก


341

ฉันมีบางหน้าพร้อมแบบฟอร์มในใบสมัครของฉัน

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



3
คำถามนี้ที่นี่ควรมีคำตอบหลังจากคุณ: stackoverflow.com/questions/1244535/ …
Nexerus


1
ที่เกี่ยวข้อง: stackoverflow.com/q/821011/435605
AlikElzin-kilaka

คำตอบ:


556

คำตอบสั้นผิด

คุณสามารถทำได้โดยจัดการกับbeforeunloadเหตุการณ์และส่งคืนสตริงที่ไม่ใช่ค่า null :

window.addEventListener("beforeunload", function (e) {
    var confirmationMessage = 'It looks like you have been editing something. '
                            + 'If you leave before saving, your changes will be lost.';

    (e || window.event).returnValue = confirmationMessage; //Gecko + IE
    return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});

ปัญหาด้วยวิธีนี้คือการส่งแบบฟอร์มนี้ยังยิงขน event สิ่งนี้ได้รับการแก้ไขอย่างง่ายดายโดยการเพิ่มการตั้งค่าสถานะที่คุณส่งแบบฟอร์ม:

var formSubmitting = false;
var setFormSubmitting = function() { formSubmitting = true; };

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting) {
            return undefined;
        }

        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';

        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};

จากนั้นเรียกตัวตั้งค่าเมื่อส่ง:

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>

แต่อ่านต่อ ...

ยาวคำตอบที่ถูกต้อง:

นอกจากนี้คุณยังไม่ต้องการที่จะแสดงข้อความนี้เมื่อผู้ใช้ไม่ได้เปลี่ยนแปลงอะไรในรูปแบบของคุณ ทางออกหนึ่งคือการใช้beforeunloadเหตุการณ์ร่วมกับการตั้งค่าสถานะ "สกปรก" ซึ่งทริกเกอร์พร้อมท์ถ้ามันเกี่ยวข้องเท่านั้น

var isDirty = function() { return false; }

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting || !isDirty()) {
            return undefined;
        }

        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';

        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};

ตอนนี้เพื่อใช้isDirtyวิธีการมีหลายวิธี

คุณสามารถใช้jQuery และการทำให้เป็นอันดับแบบฟอร์มแต่วิธีการนี้มีข้อบกพร่องบางอย่าง ก่อนอื่นคุณต้องเปลี่ยนรหัสเพื่อทำงานในรูปแบบใด ๆ ( $("form").each()จะทำ) แต่ปัญหาที่ยิ่งใหญ่ที่สุดคือ jQuery นั้นserialize()จะทำงานกับองค์ประกอบที่มีชื่อที่ไม่ได้ปิดการใช้งานเท่านั้นดังนั้นการเปลี่ยนองค์ประกอบที่ถูกปิดใช้งานหรือไม่มีชื่อใด ๆ มีวิธีแก้ปัญหาสำหรับสิ่งนั้นนั้นเช่นการควบคุมแบบอ่านอย่างเดียวแทนการเปิดใช้งานการทำให้เป็นอนุกรมแล้วปิดใช้งานการควบคุมอีกครั้ง

ดังนั้นเหตุการณ์ดูเหมือนจะเป็นไป คุณสามารถลองฟังสำหรับ keypresses เหตุการณ์นี้มีปัญหาเล็กน้อย:

  • จะไม่เรียกใช้ช่องทำเครื่องหมายปุ่มตัวเลือกหรือองค์ประกอบอื่น ๆ ที่มีการเปลี่ยนแปลงผ่านการป้อนข้อมูลของเมาส์
  • จะทำให้เกิดการกดปุ่มที่ไม่เกี่ยวข้องเช่นCtrlคีย์
  • ไม่เรียกค่าที่ตั้งผ่านโค้ด JavaScript
  • จะไม่ทำให้เกิดการตัดหรือวางข้อความผ่านเมนูบริบท
  • จะไม่ทำงานสำหรับอินพุตเสมือนเช่น datepickers หรือตัวเลือกช่องทำเครื่องหมาย / radiobutton beautifiers ซึ่งจะบันทึกค่าของพวกเขาในอินพุตที่ซ่อนอยู่ผ่าน JavaScript

changeเหตุการณ์ยังไม่ได้เรียกค่าตั้งจากโค้ด JavaScriptจึงยังจะไม่ทำงานสำหรับปัจจัยการผลิตเสมือน

การผูกinputกิจกรรมกับinputs (และtextareas ทั้งหมดselect)บนหน้าของคุณจะไม่ทำงานบนเบราว์เซอร์รุ่นเก่าและเช่นเดียวกับโซลูชันการจัดการกิจกรรมที่กล่าวถึงข้างต้นไม่สนับสนุนการเลิกทำ เมื่อผู้ใช้เปลี่ยนกล่องข้อความแล้วยกเลิกการทำเช่นนั้นหรือตรวจสอบและยกเลิกการเลือกช่องทำเครื่องหมายแบบฟอร์มยังถือว่าสกปรก

และเมื่อคุณต้องการที่จะใช้พฤติกรรมมากขึ้นเช่นการไม่สนใจองค์ประกอบบางอย่างคุณจะต้องทำงานมากขึ้น

อย่าบูรณาการล้อ:

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

หากแอปพลิเคชันของคุณใช้ jQuery อยู่แล้วคุณอาจใช้ทดสอบบำรุงรักษารหัสแทนการม้วนของคุณเองและใช้ไลบรารี่ส่วนที่สามสำหรับทั้งหมดนี้ jQuery คุณแน่ใจนะ? ปลั๊กอินทำงานที่ดีของพวกเขาดูหน้าสาธิต มันง่ายอย่างนี้:

<script src="jquery.are-you-sure.js"></script>

<script>
  $(function() {
    $('#myForm').areYouSure(
      {
        message: 'It looks like you have been editing something. '
               + 'If you leave before saving, your changes will be lost.'
      }
    );
  });

</script>

ข้อความที่กำหนดเองไม่ได้รับการสนับสนุนทุกที่

โปรดทราบว่าFirefox 4ไม่รองรับข้อความที่กำหนดเองในกล่องโต้ตอบนี้ ตั้งแต่เดือนเมษายนปี 2016 Chrome 51 กำลังเปิดตัวซึ่งข้อความที่กำหนดเองจะถูกลบออกเช่นกัน

มีบางตัวเลือกอื่นในเว็บไซต์นี้ แต่ฉันคิดว่ากล่องโต้ตอบแบบนี้ชัดเจนพอ:

คุณต้องการออกจากเว็บไซต์นี้หรือไม่?

การเปลี่ยนแปลงที่คุณทำอาจไม่ถูกบันทึก

Leave Stay


6
โปรดทราบว่าการใช้สตริงที่กำหนดเองจะไม่ทำงานอีกต่อไปใน Chrome chromestatus.com/feature/5349061406228480
2560

5
ใช่นั่นเป็นคำอธิบายในส่วนสุดท้ายของคำตอบของฉัน
CodeCaster

2
@Chris นั่นจะเป็นเพราะ Angular กำลังจัดการ DOM เพื่อเปลี่ยนหน้าในขณะที่ไม่ได้ออกไปจากเอกสารปัจจุบัน
CodeCaster

15
ถูกเตือน jQuery areYouSure ได้ถูกยกเลิกไปตั้งแต่ปี 2014 (57 ปัญหาแบบเปิดในGithub ) มันเต็มไปด้วยข้อบกพร่องจากประสบการณ์ของฉันมันอาจดูเหมือนว่ามันจะใช้ได้ในตอนแรก แต่หลังจากการทดสอบอย่างรวดเร็วฉันรู้ว่ามันไม่ได้ผลเสมอไป จะไม่แนะนำที่นี่เลย
Mark

4
@JacopoStanchi ฉันโชคไม่ดีที่ไม่พบอะไรเลย และฉันหมายถึงฉันดูค่อนข้างมาก - แต่นี่กลับมาในวันที่ 10 มกราคมดังนั้นบางคนอาจเขียนอะไรบางอย่าง แต่ฉันสงสัย ทางออกที่ดีที่สุดของคุณคือการติดตั้งด้วยตัวคุณเอง - ไม่ใช่สิ่งที่คุณต้องการจะได้ยินดังนั้นจึงเสียใจ (แต่อย่างน้อยคุณจะไม่ได้รับความประหลาดใจเหมือนที่ฉันใช้ jQuery areYouSure) และใครจะรู้ว่าคุณสามารถทำให้มันเป็นโอเพ่นซอร์สและการใช้งานของคุณจะถูกแชร์กับผู้อื่น;)
Mark

75

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


3
ฉันคิดว่ามันกลายเป็นมาตรฐานแล้ว >> เหตุการณ์ที่เกิดขึ้นเป็นครั้งแรกโดย Microsoft ใน Internet Explorer 4 และเป็นมาตรฐานในข้อกำหนด HTML5
vee

34

ผ่าน jquery

$('#form').data('serialize',$('#form').serialize()); // On load save form current state

$(window).bind('beforeunload', function(e){
    if($('#form').serialize()!=$('#form').data('serialize'))return true;
    else e=null; // i.e; if form state change show warning box, else don't show it.
});

คุณสามารถใช้ฟังก์ชั่นการทำให้เป็นแบบอนุกรมของ Google JQuery สิ่งนี้จะรวบรวมอินพุตแบบฟอร์มทั้งหมดและบันทึกลงในอาร์เรย์ ฉันคิดว่าคำอธิบายนี้เพียงพอแล้ว :)


6
อย่าใช้ #form เป็น id ของฟอร์มเพราะหน้าต่างจะสร้างและชื่อ object form :)
mdikici

1
ตรวจสอบที่นี่ถ้ามันไม่ทำงานสำหรับคุณ: stackoverflow.com/questions/7080269/…
Leniel Maccaferri

16

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

"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
    const target = evt.target;
    if (!(defaultValue in target || defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
    }
});
// detect input modifications
addEventListener("input", (evt) => {
    const target = evt.target;
    let original;
    if (defaultValue in target) {
        original = target[defaultValue];
    } else {
        original = target.dataset[defaultValue];
    }
    if (original !== ("" + (target.value || target.textContent)).trim()) {
        if (!modified_inputs.has(target)) {
            modified_inputs.add(target);
        }
    } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
    }
});
// clear modified inputs upon form submission
addEventListener("submit", () => {
    modified_inputs.clear();
    // to prevent the warning from happening, it is advisable
    // that you clear your form controls back to their default
    // state after submission
});
// warn before closing if any inputs are modified
addEventListener("beforeunload", (evt) => {
    if (modified_inputs.size) {
        const unsaved_changes_warning = "Changes you made may not be saved.";
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
    }
});
})();

1
เด็ดถั่ว เพียงคัดลอกวางรหัสนี้ในหน้าทดสอบทำการเปลี่ยนแปลงและกดรีเฟรช มันจะแสดงการเตือนเบราว์เซอร์พื้นเมืองเตือนคุณว่าคุณมีการเปลี่ยนแปลงที่ไม่ได้บันทึก
TheLegendaryCopyCoder

1
สิ่งนี้ใช้ได้กับฉันใน Chrome 71.0.3578.98 (รุ่นเป็นทางการ) (64 บิต)
JacobRossDev

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

1
เด็ดจริงๆคัดลอกและวาง ขอบคุณที่ช่วยประหยัดเวลาของฉัน ทำงานกับฉันใน Firfox 75.0
Connectify_user

1
รักตัวอย่างนี้
Rawburner

10

สร้างขึ้นจากแนวคิดที่ยอดเยี่ยมของWasim A.เพื่อใช้การทำให้เป็นอนุกรม ปัญหาที่เกิดขึ้นก็คือคำเตือนก็ปรากฏขึ้นเมื่อมีการส่งแบบฟอร์ม สิ่งนี้ได้รับการแก้ไขแล้วที่นี่

var isSubmitting = false

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

มันได้รับการทดสอบใน Chrome และ IE 11


Wasim A และ Eerik Sven Puudist - ขอบคุณฉันใช้ id สำหรับแบบฟอร์มและรหัสสำหรับปุ่มบันทึกของฉันเนื่องจากปุ่มทั้งหมดของฉันถูกพิมพ์ส่ง <form action = "" id = "Markform" วิธี = "POST"> <td> <ประเภทอินพุต = "submit" name = "submit_button" id = "save" value = "Save"> </td> <td> <input type = "submit" name = "submit_button" value = "Next"> </td> และน้อย เพิ่มเติมทุกประเภทส่งแทนที่ดังนั้น. ส่ง (โดยการคลิกเฉพาะ $ ('# บันทึก'). คลิก (ฟังก์ชั่น () {isSubmitting = true}) ใช้ '#marksform' แทน 'ฟอร์ม'
Jayanta

7

จากคำตอบก่อนหน้านี้และปูด้วยกันจากที่ต่าง ๆ ในสแต็คล้นนี่คือวิธีแก้ไขที่ฉันจัดการกับกรณีเมื่อคุณต้องการส่งการเปลี่ยนแปลงของคุณ:

window.thisPage = window.thisPage || {};
window.thisPage.isDirty = false;

window.thisPage.closeEditorWarning = function (event) {
    if (window.thisPage.isDirty)
        return 'It looks like you have been editing something' +
               ' - if you leave before saving, then your changes will be lost.'
    else
        return undefined;
};

$("form").on('keyup', 'textarea', // You can use input[type=text] here as well.
             function () { 
                 window.thisPage.isDirty = true; 
             });

$("form").submit(function () {
    QC.thisPage.isDirty = false;
});
window.onbeforeunload = window.thisPage.closeEditorWarning;

เป็นที่น่าสังเกตว่า IE11 นั้นดูเหมือนว่าจะต้องการcloseEditorWarningฟังก์ชั่นที่ส่งคืนundefinedเพื่อที่จะไม่แสดงการแจ้งเตือน


วิธีนี้ไม่ได้ตรวจสอบว่ามีการเปลี่ยนแปลงค่าใด ๆ จากสถานะดั้งเดิมของพวกเขาดังนั้นในกรณีที่ผู้ใช้ป้อนข้อความและการลบข้อความที่ป้อนข้อความก่อนที่จะพยายามที่จะนำทางไป
Brett Weber

จุดดี @ เบร็ทเวเบอร์ คุณสามารถแก้ไข closeEditorWarning ในกรณีนั้นและมี "if (window.thisPage.isDirty && uiHasChanged ())" โดยที่ uiHasChanged เป็นฟังก์ชันที่รู้เกี่ยวกับสถานะดั้งเดิมจากเซิร์ฟเวอร์และตรวจสอบความแตกต่างทั้งหมด แต่โซลูชันนี้มีไว้สำหรับ กรณีที่ฉันไม่ต้องการทำงานพิเศษทั้งหมดโดยเฉพาะและนี่เป็นผลบวกที่ผิดพลาดเมื่อมีการกดคีย์จริงใน textareas ซึ่งฉันรู้สึกว่าเป็นการประนีประนอมที่สมเหตุสมผล :)
Jonathan

ไม่ควรQC.thisPage.isDirty = false;จะเป็นwindow.thisPage.isDirty = false;? ด้วยวิธีนี้มันจะตรวจสอบว่าคุณกำลังส่งแบบฟอร์มและไม่แสดงคำเตือน ดูเหมือนว่าจะทำงานสำหรับการทดสอบของฉัน นอกจากนี้รูปแบบที่สุดมีมากกว่าinput textareaฉันใช้สิ่งต่อไปนี้ซึ่งใช้งานได้ดี:$("form").on('keyup', 'textarea,input,select', function () {
Mike

7

หนึ่งซับต่อไปนี้ได้ผลสำหรับฉัน

window.onbeforeunload = s => modified ? "" : null;

เพียงแค่ตั้งค่าmodifiedที่จะเป็นจริงหรือเท็จขึ้นอยู่กับสถานะของใบสมัครของคุณ


2
คำแนะนำ: ส่งคืนข้อความที่กำหนดเองแทนที่จะเป็นสตริงว่างเปล่าเนื่องจากบางเบราว์เซอร์จะแสดง (เช่น Edge) และส่งคืนundefinedแทนที่จะnullเป็น IE จะพิมพ์ 'null' หากmodifiedเป็นเท็จ
เควินลี

1
โทรศัพท์มือถือเป็นอย่างไร ความเข้าใจของฉันคือว่าอุปกรณ์ IOS ไม่ให้เกียรติก่อนที่จะโหลด
jaybro

4

รหัสต่อไปนี้ใช้งานได้ดี คุณต้องเข้าถึงการเปลี่ยนแปลงการป้อนข้อมูลองค์ประกอบองค์ประกอบผ่านแอตทริบิวต์ id:

var somethingChanged=false;
            $('#managerForm input').change(function() { 
                somethingChanged = true; 
           }); 
            $(window).bind('beforeunload', function(e){
                if(somethingChanged)
                    return "You made some changes and it's not saved?";
                else 
                    e=null; // i.e; if form state change show warning box, else don't show it.
            });
        });

3
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;

ฉันแทรกมันลงในไฟล์แยกต่างหากและฉันรวมมันไว้ใน <script src = "warnOnChange.js"> </script> แต่ไม่มีอะไรเกิดขึ้นเมื่อฉันแก้ไขฟิลด์ข้อความจากนั้นฉันรันคำสั่งส่วนหัว
Fabrizio Bartolomucci

2

คุณสามารถใช้ serialize () เพื่อสร้างสตริงข้อความที่เข้ารหัส URL โดยการทำให้เป็นค่าแบบฟอร์มและตรวจสอบว่าแบบฟอร์มมีการเปลี่ยนแปลงก่อนที่จะยกเลิกการโหลด

$(document).ready(function(){
    var form = $('#some-form'),
        original = form.serialize()

    form.submit(function(){
        window.onbeforeunload = null
    })

    window.onbeforeunload = function(){
        if (form.serialize() != original)
            return 'Are you sure you want to leave?'
    }
})

อ้างอิงลิงค์นี้https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form เขียนโดย Vladimir Sidorenko


1
มันทำงานได้ แต่ตามที่ตอบโดยรหัสล้อมีข้อบกพร่องบางอย่างด้วยวิธีนี้อ้างถึงคำอธิบายนี้stackoverflow.com/a/7317311/10555981
Pradeep

มีคำตอบสองสามข้อที่ใช้กันอยู่serialize()แล้วและแน่นอนว่ามันมีข้อเสีย
CodeCaster

นี่ควรเป็นคำตอบที่ถูกต้องใช้ง่ายและทำงานได้อย่างที่คาดไว้
Jodyshop

1

การเพิ่มความคิดของ @codecaster คุณสามารถเพิ่มสิ่งนี้ลงในทุก ๆ หน้าด้วยแบบฟอร์ม (ในกรณีของฉันฉันใช้มันในแบบสากลดังนั้นในแบบฟอร์มที่จะมีคำเตือนนี้เท่านั้น) เปลี่ยนหน้าที่ของเขาเป็น

if ( formSubmitting || document.getElementsByTagName('form').length == 0) 

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

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>

1

คุณสามารถตรวจสอบคำอธิบายโดยละเอียดได้ที่นี่: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/ การเปรียบเทียบรูปแบบของค่า -on-load-and -before อย่างใกล้ชิด

รหัสหลัก:

function formCompare(defaultValues, valuesOnClose) {

    // Create arrays of property names
    var aPropsFormLoad = Object.keys(defaultValues);
    var aPropsFormClose = Object.keys(valuesOnClose);

    // If number of properties is different,
    // objects are not equivalent
    if (aPropsFormLoad.length != aPropsFormClose.length) {
        return false;
    }

    for (var i = 0; i < aPropsFormLoad.length; i++) {
        var propName = aPropsFormLoad[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;

}

//add polyfill for older browsers, as explained on the link above

//use the block below on load
    for(i=0; i < document.forms[0].elements.length; i++){
    console.log("The field name is: " + document.forms[0].elements[i].name +
        " and it’s value is: " + document.forms[0].elements[i].value );
    aPropsFormLoad[i] = document.forms[0].elements[i].value;
    }

//create a similar array on window unload event.

//and call the utility function
    if (!formCompare(aPropsOnLoad, aPropsOnClose))
    {
    //perform action: 
    //ask user for confirmation or
    //display message about changes made
    }


1

การแก้ปัญหาโดย Eerik Sven Puudist ...

var isSubmitting = false;

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

... โดยธรรมชาติแล้วฉันทำงานในการตั้งค่าเชิงวัตถุที่ซับซ้อนโดยไม่มีการเปลี่ยนแปลงใด ๆ ที่จำเป็น

การเปลี่ยนแปลงเพียงอย่างเดียวที่ฉันนำมาใช้คือการอ้างถึงรูปแบบที่เป็นรูปธรรม (หนึ่งรูปแบบต่อไฟล์) ที่เรียกว่า "formForm" ('form' -> '#formForm'):

<form ... id="formForm" name="formForm" ...>

ทำได้ดีมากโดยเฉพาะอย่างยิ่งความจริงที่ว่าปุ่มส่งกำลังถูก "เหลือคนเดียว"

นอกจากนี้มันใช้งานได้กับฉันด้วย Firefox เวอร์ชันล่าสุด (จนถึงวันที่ 7 กุมภาพันธ์ 2019)


1

โซลูชันสากลของ Eli Grey ที่ทดสอบแล้วทำงานได้หลังจากที่ฉันทำให้โค้ดง่ายขึ้นเท่านั้น

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

  })();

การดัดแปลงของเขาจะถูกลบการใช้งานtarget[defaultValue]และการใช้งานเท่านั้นtarget.dataset[defaultValue]เพื่อเก็บค่าเริ่มต้นจริงเท่านั้น

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

แต่โซลูชัน 'สากล' นี้ใช้ได้กับเบราว์เซอร์เท่านั้นไม่สามารถใช้งานได้ในมุมมองเว็บของแอปตัวอย่างเช่น wechat เบราว์เซอร์

ในการทำให้เบราว์เซอร์ wechat (บางส่วน) ทำงานได้ดีขึ้นอีก:

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }

      if(modified_inputs.size){
        const event = new Event('needSave')
        window.dispatchEvent(event);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

    const ua = navigator.userAgent.toLowerCase();

    if(/MicroMessenger/i.test(ua)) {
      let pushed = false

      addEventListener('needSave', evt => {
        if(!pushed) {
          pushHistory();

          window.addEventListener("popstate", function(e) {
            if(modified_inputs.size) {
              var cfi = confirm('确定要离开当前页面嘛?' + JSON.stringify(e));
              if (cfi) {
                modified_inputs.clear()
                history.go(-1)
              }else{
                e.preventDefault();
                e.stopPropagation();
              }
            }
          }, false);
        }

        pushed = true
      });
    }

    function pushHistory() {
      var state = {
        title: document.title,
        url: "#flag"
      };
      window.history.pushState(state, document.title, "#flag");
    }
  })();

0

ฉันทำมันแตกต่างกันแบ่งปันที่นี่เพื่อให้ใครบางคนสามารถรับความช่วยเหลือทดสอบกับ Chrome เท่านั้น

ฉันต้องการเตือนผู้ใช้ก่อนที่จะปิดแท็บเฉพาะเมื่อมีการเปลี่ยนแปลงบางอย่าง

<input type="text" name="field" value="" class="onchange" />

var ischanged = false;

$('.onchange').change(function () {
    ischanged = true;
});

window.onbeforeunload = function (e) {
    if (ischanged) {
        return "Make sure to save all changes.";
    }        
};

ทำงานได้ดี แต่เกิดปัญหาอื่นเมื่อฉันส่งแบบฟอร์มที่ฉันได้รับการเตือนที่ไม่พึงประสงค์ฉันเห็นจำนวนของการแก้ปัญหาในเรื่องนี้เป็นเพราะไฟ onbeforeunload ก่อนครับ onsubmit ทำไมเราไม่สามารถจัดการกับมันในกรณี onsubmit เหมือนonbeforeunload = nullแต่ เหตุการณ์ onclick ของปุ่มส่งเริ่มก่อนเหตุการณ์ทั้งสองนี้ดังนั้นฉันจึงอัปเดตโค้ด

var isChanged = false;
var isSubmit = false;

window.onbeforeunload = function (e) {
    if (isChanged && (!isSubmit)) {
        return "Make sure to save all changes.";
    }        
};

$('#submitbutton').click(function () {
    isSubmit = true;
});

$('.onchange').change(function () {
    isChanged = true;
});

-5

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


คุณจะถูก แต่รูปแบบของฉันถูกสร้างขึ้นแบบไดนามิกและเขตข้อมูลไม่ได้อยู่ในการควบคุมของฉันดังนั้นสำหรับฉันมันเป็นไปไม่ได้ที่จะติดตามทุกสาขาและส่งคำขอผ่าน ajax หากคุณมีบางอย่างในใจกรุณาแบ่งปันกับฉัน
Khan

โอเคอาจไม่จำเป็นสำหรับวัตถุประสงค์ของคุณ แต่ก็ยังสามารถนำไปใช้ได้ หากองค์ประกอบของแบบฟอร์มอยู่บนหน้าจอคุณสามารถแนบตัวจัดการเหตุการณ์ของคุณเองและรับข้อมูลใด ๆ ที่คุณต้องการ จากนั้นใช้ AJAX เพื่อบันทึกข้อมูลไปยังตำแหน่งที่คุณต้องการ แม้ว่าฟอร์มจะมี ID หรือชื่อคลาสที่สร้างแบบสุ่มคุณสามารถใช้ XPaths ได้หากคุณคาดการณ์โครงสร้างของฟอร์ม เป็น XML / HTML ใช่มั้ย ดังนั้นคุณสามารถมี JAD กับผู้ที่รับผิดชอบในรูปแบบที่สร้างขึ้นแบบไดนามิกและพูดคุยเกี่ยวกับวิธีที่ XSTL จะเป็นประโยชน์ต่อคุณทั้งสองในโครงการนี้ แค่คิด ...
SimonDever

1
save it on any change without waiting any submitting from userคุณไม่ต้องการทำเช่นนี้เสมอไป บางครั้งผู้ใช้ต้องการเปลี่ยนแปลงหลายอย่างจากนั้นตรวจสอบและยืนยันว่าต้องการบันทึก
BadHorsie

1
ฉันไม่แนะนำให้ส่งแบบฟอร์ม ข้อมูลอาจถูกบันทึกใน localStorage ในแคชบางส่วนหรืออาจถูกส่งด้วยแฟล็กแบบร่าง มีวิธีการมากมายสำหรับพฤติกรรมนี้ มันดีกว่าการเตือนผู้ใช้ว่าเขาไม่ได้กรอกแบบฟอร์มให้เสร็จและถ้าเขาไม่ทำและจะปิดหน้าต่างเขาจะต้องกรอกข้อมูลอีกครั้งในครั้งต่อไป "มาเลยคนไม่ต้องขี้เกียจรถไฟ / รถบัส / เครื่องบิน / ฯลฯ ไม่สำคัญเช่นกันเพราะเราไม่สนใจว่าคุณลืมที่ชาร์จจากแล็ปท็อปของคุณเพียงกรอกแบบฟอร์มแช่ง!"
YuS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.