使用jQuery编辑表单时确认离开页面

19

我正在编写一个表单,需要一个类似stackoverflow的函数:“您已经开始编写或编辑帖子。”。

我已经查看了stackoverflow的代码,但是我不知道如何将其应用到我的简单表单中。这是他们在question.js文件中的代码:

function initNavPrevention(b) {
    if (b.which == "undefined") {
        return
    }
    var a = $("#wmd-input");
    a.unbind("keypress", initNavPrevention);
    setConfirmUnload("You have started writing or editing a post.", a)
}

其中一个函数会触发这个操作(我不知道 c 是什么):

if (c) {
    e.keypress(initNavPrevention)
}

最后,他们使用setConfirmUnload(null);来禁用它,我猜是这样。

我的情况很简单。我有一个页面,在该页面上通过AJAX加载了一个<form />,我想防止当加载这个表单时,用户在不点击提交按钮或者如果用户点击取消,则禁用该表单,从而防止离开该页面。

有了这段代码,有人能告诉我如何将其包含在我的网站中吗?

这是我用于加载表单的AJAX函数:

$("#projects_historics_edit_part_1").click(function(){
    $("#ajax-loader").fadeIn('normal');

    $.ajax({
        url: BASE_URL + 'projects/ajax/get_historics_edit_part_1/' + PROJECT_ID,
        type: "POST",
        success: function(data) {
            $("div#projects_historics_part_1").html(data);

            $("#client").autocomplete(client_autcomplete_options);

            var inicofin = $("#initdate, #findate").datepicker(initdate_datepicker_options);
        }
    });

    $("#ajax-loader").fadeOut("normal");

    return false;    
});

提前谢谢!


5个回答

32

你可以使用 window.onbeforeunload 来实现这个功能:

window.onbeforeunload = function() {
    return 'Are you sure you want to navigate away from this page?';
};

所以在您的代码中,您将把它放在$.ajax调用的success回调函数内。

要取消确认,您可以执行以下操作:

window.onbeforeunload = null;

另外,请查看这个回答:如何覆盖OnBeforeUnload对话框并用自己的替代它?


但是如果我使用它,即使使用提交按钮提交表单,消息也会显示。我想在离开页面而没有提交表单时才显示该消息。 - FoxInFlame
2
使用 window.onbeforeunload = undefined; 取消确认。它也适用于IE。特别是如果您不需要显示消息,可以返回未定义(将适用于IE和其他浏览器)。 - Mina Matta

15

这是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 box not.
});
你可以谷歌 JQuery 表单序列化函数,它会收集所有表单输入并保存在数组中。我想这个解释已经足够了 :)

1
有时您通过ajax提交数据,而没有标签属性名称,在这种情况下,请使用$(form).html()。 - Wasim A.
刚才我正在思考那个问题 :) - K. Kilian Lindberg
这是一个非常好的方法。我使用了循环和$('form').each(...),而不是$('#form'),以防页面上有多个表单。我已经添加了我的替代方案作为另一个答案,因为它并不是那么明显。但所有的赞誉都归于你! - Sam Watkins
很棒的代码,谢谢!有没有办法自定义弹出框中显示的文本? - Cojones
谢谢,但我认为我们无法自定义浏览器的那些对象。 - Wasim A.

3

我根据Wasim的出色答案编写了一个通用函数:

var confirm_nav_ignore = [];
function confirm_nav() {
    function save_form_state($form) {
        var old_state = $form.serialize();
        $form.data('old_state', old_state);
    }

    // On load, save form current state
    $('form').each(function() {
        save_form_state($(this));
    });

    // On submit, save form current state
    $('form').submit(function() {
        save_form_state($(this));
    });

    // strip fields that we should ignore
    function strip(form_data) {
        for (var i=0; i<confirm_nav_ignore.length; i++) {
            var field = confirm_nav_ignore[i];
            form_data = form_data.replace(new RegExp("\\b"+field+"=[^&]*"), '');
        }   
        return form_data;
    }

    // Before unload, confirm navigation if any field has been edited
    $(window).on('beforeunload', function(e) {
        var rv;
        $('form').each(function() {
            var $form = $(this);
            var old_state = strip($form.data('old_state'));
            var new_state = strip($form.serialize());
            if (new_state != old_state) {
                rv = '';
            }   
        }); 
        return rv;
    }); 
}

// I call it at the end of the on-load handler:
$(function() {
    confirm_nav();
});

变更:

  1. 允许用户在不确认的情况下提交表单。
  2. 支持多个表单,使用$('form').each(...)。
  3. 不再依赖于表单的id为"form"。
  4. 返回''作为自定义确认消息,而不是true。如果返回true,在Chrome中会显示"true"。
  5. 对于复杂的页面,可以告诉它忽略某些表单字段。
  6. 最好从on-load处理程序的结尾运行它。
  7. 足够通用,可以在站点的每个页面上使用。

看起来可能有些过于工程化,但这是我在一个真实的Web应用程序中使其正常运行所必须做的事情。

感谢Wasim提出的使用JQuery $(form).serialize()的好方法。


0

基于Wasim A.的回答。那段代码看起来很棒,但是如果我点击提交按钮,我也会收到通知。我认为这样更好:

$(document).ready(function() {
    let form = $('#form');
    form.data('serialize', form.serialize());
    
    // if submit clicked - pass user to save form
    form.find('submit').on('click', function () {
        $(window).off('beforeunload');
    });
});



  // On load save form current state

$(window).bind('beforeunload', function(e){
    let form = $('#form');
    if(form.serialize() !== form.data('serialize')) {
      return true;
    } else {
    // i.e; if form state change show box not.
      e=null;
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


0

对优秀的Sam答案的小优化:

var confirm_nav_ignore = [];
function confirm_nav() {
    var $forms = $('form'); // if you need it add .filter('#your_form_id');

    function save_form_state() {
        var $form = $(this),
            old_state = $form.serialize();
        $form.data('old_state', old_state);
    }

    $forms
        // On load, save form current state
        .each(save_form_state)
        // On submit, save form current state
        .submit(save_form_state)
    ;

    // strip fields that we should ignore
    function strip(form_data) {
        for (var i = 0; i < confirm_nav_ignore.length; i++) {
            var field = confirm_nav_ignore[i];
            form_data = form_data.replace(new RegExp("\\b" + field + "=[^&]*"), '');
        }

        return form_data;
    }

    // Before unload, confirm navigation if any field has been edited
    $(window).on('beforeunload', function(e) {
        var rv = undefined;

        $forms.each(function() {
            var $form = $(this);
            var old_state = strip($form.data('old_state'));
            var new_state = strip($form.serialize());
            if (new_state !== old_state) {
                e.preventDefault();
                rv = '';
            }
        });

        return rv;
    });
}
// I call it at the end of the on-load handler:
$(confirm_nav);

已在最新版本的Chrome、Firefox和Safari上进行了测试


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接