Cảnh báo người dùng trước khi rời khỏi trang web với những thay đổi chưa được lưu


340

Tôi có một số trang với các hình thức trong ứng dụng của tôi.

Làm cách nào tôi có thể bảo mật biểu mẫu theo cách mà nếu ai đó điều hướng đi hoặc đóng tab trình duyệt, họ sẽ được nhắc xác nhận rằng họ thực sự muốn rời khỏi biểu mẫu với dữ liệu chưa được lưu?



3
Câu hỏi này ở đây sẽ có câu trả lời cho bạn sau: stackoverflow.com/questions/1244535/
mẹo


Câu trả lời:


556

Câu trả lời ngắn gọn:

Bạn có thể làm điều này bằng cách xử lý beforeunloadsự kiện và trả về một chuỗi không 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.
});

Vấn đề với cách tiếp cận này là việc gửi biểu mẫu cũng đang kích hoạt sự kiện dỡ tải . Điều này được khắc phục dễ dàng bằng cách thêm cờ bạn đang gửi biểu mẫu:

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

Sau đó gọi setter khi gửi:

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

Nhưng hãy đọc tiếp ...

Câu trả lời dài, đúng:

Bạn cũng không muốn hiển thị thông báo này khi người dùng không thay đổi bất cứ điều gì trên biểu mẫu của bạn . Một giải pháp là sử dụng beforeunloadsự kiện kết hợp với cờ "bẩn", chỉ kích hoạt lời nhắc nếu nó thực sự có liên quan.

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

Bây giờ để thực hiện isDirtyphương pháp, có nhiều cách tiếp cận khác nhau.

Bạn có thể sử dụng jQuery và hình thành tuần tự hóa , nhưng cách tiếp cận này có một số sai sót. Trước tiên, bạn phải thay đổi mã để hoạt động trên bất kỳ hình thức nào ( $("form").each()sẽ làm), nhưng vấn đề lớn nhất là jQuery serialize()sẽ chỉ hoạt động trên các phần tử có tên, không bị vô hiệu hóa, do đó, việc thay đổi bất kỳ phần tử bị vô hiệu hóa hoặc không tên nào sẽ không kích hoạt cờ bẩn. Có cách giải quyết cho vấn đề đó , như làm cho các điều khiển chỉ đọc thay vì bật, tuần tự hóa và sau đó vô hiệu hóa lại các điều khiển.

Vì vậy, các sự kiện dường như con đường để đi. Bạn có thể thử nghe nhấn phím . Sự kiện này có một vài vấn đề:

  • Không kích hoạt các hộp kiểm, nút radio hoặc các yếu tố khác đang được thay đổi thông qua đầu vào chuột.
  • Sẽ kích hoạt cho các phím nhấn không liên quan như Ctrlphím.
  • Không kích hoạt các giá trị được đặt thông qua mã JavaScript.
  • Không kích hoạt cắt hoặc dán văn bản thông qua các menu ngữ cảnh.
  • Không hoạt động đối với các đầu vào ảo như bộ tạo ngày hoặc hộp làm đẹp hộp kiểm / radiobutton để lưu giá trị của chúng trong một đầu vào ẩn thông qua JavaScript.

Sự changekiện cũng không kích hoạt các giá trị được đặt từ mã JavaScript , do đó cũng sẽ không hoạt động đối với các đầu vào ảo.

Liên kết inputsự kiện với tất cả input(và textareas và selects) trên trang của bạn sẽ không hoạt động trên các trình duyệt cũ hơn và, giống như tất cả các giải pháp xử lý sự kiện được đề cập ở trên, không hỗ trợ hoàn tác. Khi người dùng thay đổi hộp văn bản và sau đó hoàn tác, hoặc kiểm tra và bỏ chọn hộp kiểm, biểu mẫu vẫn bị coi là bẩn.

Và khi bạn muốn thực hiện nhiều hành vi hơn, như bỏ qua các yếu tố nhất định, bạn sẽ còn nhiều việc phải làm.

Đừng phát minh lại bánh xe:

Vì vậy, trước khi bạn nghĩ về việc thực hiện các giải pháp đó và tất cả các giải pháp cần thiết, hãy nhận ra rằng bạn đang phát minh lại bánh xe và bạn có xu hướng gặp phải các vấn đề mà người khác đã giải quyết cho bạn.

Nếu ứng dụng của bạn đã sử dụng jQuery, bạn cũng có thể sử dụng mã được kiểm tra, bảo trì thay vì tự cuộn và sử dụng thư viện phần thứ ba cho tất cả những điều này. Bạn có chắc không? Plugin hoạt động tuyệt vời, xem trang demo của họ . Nó đơn giản như thế này:

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

Tin nhắn tùy chỉnh không được hỗ trợ ở mọi nơi

Xin lưu ý rằng Firefox 4 không hỗ trợ các thông báo tùy chỉnh trong hộp thoại này. Kể từ tháng 4 năm 2016, Chrome 51 đang được triển khai trong đó các thông báo tùy chỉnh cũng đang bị xóa .

Một số lựa chọn thay thế tồn tại ở nơi khác trên trang web này, nhưng tôi nghĩ một hộp thoại như thế này là đủ rõ ràng:

Bạn có muốn rời khỏi trang web này?

Những thay đổi bạn thực hiện có thể không được lưu.

Leave Stay


6
Lưu ý rằng việc sử dụng chuỗi tùy chỉnh sẽ không hoạt động nữa trong Chrome; chromestatus.com/feature/5349061406228480
amann

5
Vâng, đó là mô tả trong phần cuối của câu trả lời của tôi.
CodeCaster

2
@Chris đó sẽ là vì Angular đang thao túng DOM để thay đổi trang, trong khi không điều hướng khỏi tài liệu hiện tại.
CodeCaster

15
Được cảnh báo jQuery làYouSure đã bị bỏ rơi từ năm 2014 (57 vấn đề mở trên Github ), nó đầy lỗi từ trải nghiệm của tôi, có vẻ như nó hoạt động lúc đầu nhưng sau một số thử nghiệm nhanh tôi nhận ra nó không hoạt động. Sẽ không đề xuất điều này chút nào
Đánh dấu

4
@JacopoStanchi Tôi không may tìm thấy bất kỳ. Và ý tôi là tôi trông khá nhiều - nhưng điều này đã trở lại vào ngày 10 tháng 1 để ai đó có thể đã viết một cái gì đó nhưng tôi nghi ngờ nó. Đặt cược tốt nhất của bạn là tự thực hiện nó - đó không phải là điều bạn muốn nghe tho xin lỗi. (Nhưng ít nhất bạn sẽ không nhận được bất kỳ sự ngạc nhiên như tôi đã sử dụng jQuery areYouSure.) Ngoài ra ai mà biết được có lẽ bạn thậm chí có thể làm cho nó mã nguồn mở và thực hiện của bạn sẽ được chia sẻ với những người khác;)
Đánh dấu

75

Kiểm tra sự kiện onb Beforeunload JavaScript . Đó là JavaScript không chuẩn do Microsoft giới thiệu, tuy nhiên, nó hoạt động trong hầu hết các trình duyệt và tài liệu onb Beforeunload của họ có nhiều thông tin và ví dụ hơn.


3
Tôi nghĩ rằng nó đã trở thành tiêu chuẩn bây giờ. >> Sự kiện ban đầu được Microsoft giới thiệu trong Internet Explorer 4 và được chuẩn hóa trong đặc tả HTML5.
vee

34

thông qua 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.
});

Bạn có thể hàm Google JQuery Form serialize, điều này sẽ thu thập tất cả các đầu vào của biểu mẫu và lưu nó thành mảng. Tôi đoán điều này giải thích là đủ :)


6
không sử dụng #form làm id mẫu vì cửa sổ sẽ tạo và đối tượng có tên là mẫu :)
mdikici

1
Kiểm tra ở đây nếu nó không hiệu quả với bạn: stackoverflow.com/questions/7080269/NH
Leniel Maccaferri

16

Giải pháp phổ quát không yêu cầu cấu hình tự động phát hiện tất cả các sửa đổi đầu vào, bao gồm các yếu tố có thể nội dung:

"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
Đậu nguội. Chỉ cần sao chép dán mã này vào trang thử nghiệm của bạn, thực hiện thay đổi và nhấn refresh. Nó sẽ hiển thị cảnh báo trình duyệt riêng cảnh báo bạn rằng bạn có các thay đổi chưa được lưu.
TheLegendaryCopyCoder

1
Điều này hoạt động với tôi trên Chrome 71.0.3578.98 (Bản dựng chính thức) (64-bit)
JacobRossDev

2
Điều này hoạt động thực sự tốt, nhưng nếu bạn đang sử dụng biểu mẫu này để gửi, bạn sẽ cần xóa phần sửa đổi được đặt trước trước nếu không bạn sẽ nhận được cảnh báo thay đổi xác nhận. Tôi đã thêm một trình lắng nghe sự kiện vào biểu mẫu của mình để gọi khi gửi và chỉ cần chạy chức năng thiết lập rõ ràng, theo cách đó, mod_inputs rõ ràng khi trình nghe sự kiện trước khi chạy và cho phép bài đăng tiếp tục.
stepquick

1
Mát mẻ thực sự sao chép và dán. cảm ơn vì đã tiết kiệm thời gian của tôi Làm việc cho tôi trong Firfox 75.0
Connectify_user

1
Yêu đoạn trích này
Rawburner

10

Được xây dựng dựa trên ý tưởng tuyệt vời của Wasim A. để sử dụng tuần tự hóa. Vấn đề ở đây là cảnh báo cũng được hiển thị khi biểu mẫu đang được gửi. Điều này đã được sửa ở đây.

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

Nó đã được thử nghiệm trong Chrome và IE 11.


Wasim A và Eerik Sven Puudist - Cảm ơn tôi đã sử dụng id cho biểu mẫu và id cho nút lưu của mình vì tất cả các nút của tôi là loại gửi <form action = "" id = "markform" method = "POST"> <td> <input type = "submit" name = "submit_button" id = "save" value = "Save"> </ td> <td> <input type = "submit" name = "submit_button" value = "Next"> </ td> và vài nhiều hơn tất cả các loại gửi Do đó được thay thế .Submit (bằng cách nhấp cụ thể $ ('# save'). click (function () {isSubmmit = true}) Sử dụng '#markform' thay vì 'form'
Jayanta

7

Dựa trên các câu trả lời trước đó và được ghép lại từ nhiều vị trí khác nhau trong ngăn xếp chồng, đây là giải pháp tôi đưa ra để xử lý trường hợp khi bạn thực sự muốn gửi thay đổi của mình:

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;

Điều đáng chú ý là IE11 dường như yêu cầu closeEditorWarninghàm trả về undefinedcho nó không hiển thị cảnh báo.


Giải pháp này không xác minh rằng bất kỳ giá trị nào được thay đổi từ trạng thái ban đầu của chúng, do đó vô hiệu hóa trong trường hợp người dùng nhập văn bản và xóa văn bản đã nhập trước khi cố gắng điều hướng đi.
Brett Weber

Điểm hay, @BrettWeber. Bạn có thể sửa đổi closeEditorWarning trong trường hợp đó và có "if (window.thisPage.isDenty && uiHasChanged ())", trong đó uiHasChanged là một hàm biết về trạng thái ban đầu từ máy chủ và kiểm tra tất cả các khác biệt, nhưng giải pháp này là cho một trường hợp mà tôi không đặc biệt muốn làm tất cả công việc làm thêm đó và điều này chỉ có những điểm sai khi nhấn phím thực tế đã được thực hiện trong văn bản, mà tôi cảm thấy là một sự thỏa hiệp hợp lý. :)
Jonathan

Không nên QC.thisPage.isDirty = false;window.thisPage.isDirty = false;? Bằng cách đó, nó sẽ kiểm tra xem bạn có đang gửi biểu mẫu không và không hiển thị cảnh báo. Dường như để làm việc cho các bài kiểm tra của tôi. Ngoài ra, hầu hết các hình thức có một inputchứ không phải là a textarea. Tôi đã sử dụng những thứ sau đây hoạt động khá tốt:$("form").on('keyup', 'textarea,input,select', function () {
Mike

7

Các lớp lót sau đây đã làm việc cho tôi.

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

Chỉ cần đặt modifiedthành đúng hoặc sai tùy thuộc vào trạng thái của ứng dụng của bạn.


2
Một số gợi ý: Trả về một thông báo tùy chỉnh thay vì một chuỗi trống vì một số trình duyệt sẽ hiển thị nó (ví dụ: Edge) và trả về undefinedthay nullvì IE sẽ in 'null' nếu modifiedsai.
Kevin Lee

1
Làm thế nào về thiết bị di động? sự hiểu biết của tôi là các thiết bị iOS không tôn trọng onb Beforeunload.
jaybro

4

Mã sau hoạt động tuyệt vời. Bạn cần đạt được các thay đổi đầu vào của phần tử biểu mẫu thông qua thuộc tính 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;

Tôi đã chèn nó vào một tệp riêng biệt và tôi đã bao gồm nó với <script src = "warnOnChange.js"> </ script> nhưng không có gì xảy ra khi tôi sửa đổi trường văn bản và sau đó tôi thực thi lệnh tiêu đề.
Fabrizio Bartolomucci

2

Bạn có thể sử dụng serialize () để tạo chuỗi văn bản được mã hóa URL bằng cách tuần tự hóa các giá trị của biểu mẫu và kiểm tra xem biểu mẫu đã thay đổi trước khi tải chưa

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

Tham khảo liên kết này https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsatted-form Viết bởi Vladimir Sidorenko


1
Nó hoạt động, nhưng như được trả lời bởi mã caster, có một số sai sót với phương thức này, hãy tham khảo giải thích này stackoverflow.com/a/7317311/10555981
Pradeep

Đã có một vài câu trả lời bằng cách sử dụng serialize(), và thực sự, nó có nhược điểm của nó.
CodeCaster

Đây phải là câu trả lời chính xác, rất đơn giản để sử dụng và hoạt động như mong đợi.
Jodyshop

1

Thêm vào ý tưởng của @codecaster, bạn có thể thêm nó vào mỗi trang bằng một biểu mẫu (trong trường hợp của tôi, tôi sử dụng nó theo cách toàn cầu để chỉ trên các biểu mẫu mới có cảnh báo này) thay đổi chức năng của mình thành

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

Đồng thời đặt các biểu mẫu gửi bao gồm đăng nhập và trong các nút hủy liên kết để khi người đó nhấn hủy hoặc gửi biểu mẫu sẽ không kích hoạt cảnh báo trong mỗi trang với một biểu mẫu ...

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

1

Bạn có thể kiểm tra một lời giải thích chi tiết tại đây: http://techinvestigations.redapi.in/comparison-of-form-values-on-load-and-b Before-close / - so sánh về hình thức-giá trị-tải-và trước khi đóng

Mã chính:

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

Câu trả lời ngắn:

let pageModified = true

window.addEventListener("beforeunload", 
  () => pageModified ? 'Close page without saving data?' : null
)

1

Giải pháp của 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.'
        }
    });
})

... Tự nhiên làm công việc cho tôi trong một thiết lập hướng đối tượng phức tạp mà không có bất kỳ thay đổi cần thiết nào.

Thay đổi duy nhất tôi áp dụng là đề cập đến biểu mẫu cụ thể (chỉ một biểu mẫu cho mỗi tệp) được gọi là "formForm" ('biểu mẫu' -> '#formForm'):

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

Đặc biệt được thực hiện tốt là thực tế là nút gửi đang bị "bỏ lại một mình".

Ngoài ra, nó cũng hoạt động với tôi với phiên bản Firefox mới nhất (kể từ ngày 7 tháng 2 năm 2019).


1

Đã thử nghiệm giải pháp phổ quát của Eli Grey, chỉ hoạt động sau khi tôi đơn giản hóa mã thành

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

  })();

Các sửa đổi đối với mình bị xóa việc sử dụng target[defaultValue]và chỉ sử dụngtarget.dataset[defaultValue] để lưu trữ giá trị mặc định thực sự.

Và tôi đã thêm một trình lắng nghe sự kiện 'đã lưu' trong đó sự kiện 'đã lưu' sẽ được kích hoạt bởi chính bạn trên hành động lưu của bạn đã thành công.

Nhưng giải pháp 'phổ quát' này chỉ hoạt động trong các trình duyệt, không hoạt động trong chế độ xem web của ứng dụng, ví dụ: các trình duyệt wechat.

Để làm cho nó hoạt động trong trình duyệt wechat (một phần), một lần nữa cải tiến:

  '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

Tôi đã làm khác đi, chia sẻ ở đây để ai đó có thể nhận trợ giúp, chỉ được thử nghiệm với Chrome.

Tôi muốn cảnh báo người dùng trước khi đóng tab chỉ khi có một số thay đổi.

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

Hoạt động tốt, nhưng có một vấn đề khác, khi tôi gửi biểu mẫu tôi nhận được cảnh báo không mong muốn, tôi đã thấy rất nhiều cách giải quyết, đó là vì onb Beforeunload bị cháy trước khi đưa ra rằng tại sao chúng ta không thể xử lý nó trong sự kiện onsubmit như onbeforeunload = null, nhưng sự kiện onclick của nút gửi kích hoạt trước cả hai sự kiện này, vì vậy tôi đã cập nhật mã

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

Trước hết, hầu hết các trình duyệt có chức năng này theo mặc định. Và tại sao bạn cần điều này cả? Tại sao không giữ hình thức đồng bộ hóa? Ý tôi là, lưu nó vào bất kỳ thay đổi nào mà không cần chờ bất kỳ sự đệ trình nào từ người dùng. Giống như Danh bạ Google làm. Tất nhiên nếu chỉ có tất cả các lĩnh vực trong hình thức là bắt buộc. Người dùng không thích khi họ buộc phải lấp đầy thứ gì đó mà không có cơ hội bỏ đi để suy nghĩ nếu họ cần. :)


bạn có đúng không nhưng các biểu mẫu của tôi được tạo ra một cách linh hoạt và các trường không nằm trong tầm kiểm soát của tôi vì vậy đối với tôi không thể theo dõi tất cả các trường và gửi yêu cầu qua ajax, nếu bạn có điều gì đó trong tâm trí, vui lòng chia sẻ với tôi.
Khan

Được rồi nó có thể không cần thiết cho mục đích của bạn nhưng nó vẫn có thể được thực hiện. Nếu các thành phần biểu mẫu nằm trên màn hình, bạn có thể đính kèm trình xử lý sự kiện của riêng mình và nhận bất kỳ dữ liệu nào bạn muốn. Sau đó sử dụng AJAX để lưu dữ liệu đến bất cứ nơi nào bạn muốn. Ngay cả khi các biểu mẫu có ID hoặc tên lớp được tạo ngẫu nhiên, bạn có thể sử dụng XPath nếu bạn có thể dự đoán cấu trúc của biểu mẫu. Đó là XML / HTML phải không? Vì vậy, bạn thậm chí có thể có một JAD với những người chịu trách nhiệm về hình thức được tạo động và nói về cách XSTL có thể mang lại lợi ích cho bạn trong cả dự án này. Chỉ cần một suy nghĩ ...
SimonDever

1
save it on any change without waiting any submitting from userBạn không luôn muốn làm điều này. Đôi khi người dùng muốn thực hiện nhiều thay đổi và sau đó xem xét và xác nhận rằng họ muốn lưu nó.
BadHorsie

1
Tôi đã không đề nghị gửi biểu mẫu. Dữ liệu có thể được lưu trong localStorage, trong một số bộ đệm hoặc thậm chí được gửi cùng với một số cờ dự thảo. Có nhiều phương pháp cho hành vi này. Sẽ tốt hơn nhiều so với cảnh báo người dùng về việc anh ta đã không hoàn thành biểu mẫu và nếu anh ta không và sẽ đóng cửa sổ, anh ta sẽ phải điền lại vào lần sau. "Thôi nào, anh bạn, đừng lười biếng, xe lửa / xe buýt / máy bay / v.v. của bạn không quan trọng cũng như chúng tôi không quan tâm rằng bạn đã quên bộ sạc từ máy tính xách tay của mình, chỉ cần điền vào mẫu đơn chết tiệt này!"
YuS
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.