我的申请表里有几页表格。

如何确保表单的安全性,以便在用户导航离开或关闭浏览器选项卡时,提示他们确认是否确实希望保留未保存数据的表单?


当前回答

您可以使用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 作者:弗拉基米尔·西多伦科

其他回答

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

你可以谷歌JQuery表单序列化函数,这将收集所有表单输入并保存在数组中。我想这个解释就足够了:)

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(截至2019年2月7日)。

我做了下面的代码。它可以比较所有字段的变化(除了那些用. ignoredirty类标记的字段),也可以只比较当前可见的字段。它可以为Javascript添加的新字段重新初始化。因此,我保存的不是表单状态,而是每个控件的状态。

/* Dirty warning for forms */
dirty = (skipHiddenOrNullToInit) => {
    /*  will return True if there are changes in form(s)
        for first initialization you can use both: .dirty(null) or .dirty() (ignore its result)
            .dirty(null) will (re)initialize all controls - in addititon use it after Save if you stay on same page
            .dirty() will initialize new controls - in addititon use it if you add new fields with JavaScript
        then
            .dirty() (or: .dirty(false)) says if data are changed without regard to hidden fields
            .dirty(true) says if data are changed with regard to hidden fields (ie. fields with .d-none or .hidden class)
        controls with .ignoreDirty class will be skipped always
        previous about .d-none, .hidden, .ignoreDirty applies to the control itself and all its ancestors
    */
    let isDirty = false;
    let skipSelectors = '.ignoreDirty';
    if (skipHiddenOrNullToInit) {
        skipSelectors += ', .d-none, .hidden'
    } else if (skipHiddenOrNullToInit === undefined) {
        skipHiddenOrNullToInit = false;
    }
    $('input, select').each(
    function(_idx, el) {
        if ($(el).prop('type') !== 'hidden') {
            let dirtyInit = $(el).data('dirty-init');
            if (skipHiddenOrNullToInit === null || dirtyInit === undefined) {
                try {
                    isChromeAutofillEl = $(el).is(":-webkit-autofill");
                } catch (error) {
                    isChromeAutofillEl = false;
                }
                if (isChromeAutofillEl && $(el).data('dirty-init') === undefined) {
                    setTimeout(function() {  // otherwise problem with Chrome autofilled controls
                        $(el).data('dirty-init', $(el).val());
                    }, 200)
                } else {
                    $(el).data('dirty-init', $(el).val());
                }
            } else if ($(el).closest(skipSelectors).length === 0 && dirtyInit !== $(el).val()) {
                isDirty = true;
                return false; // breaks jQuery .each
            }
        }
    }
    );
    return isDirty;
}

我有额外的Chrome自动填充值的麻烦,因为它很难初始化和已经加载它们。所以我不初始化页面加载,但在任何focusin事件。(但是:也许JavaScript更改控件值仍然存在问题。)我使用下面的代码,我在页面加载调用:

let init_dirty = (ifStayFunc) => {
    /*  ifStayFunc: optional callback when user decides to stay on page
    use .clearDirty class to avoid warning on some button, however:
        if the button fires JavaScript do't use .clearDirty class and instead
            use directly dirty(null) in code - to be sure it will run before window.location */
    $('input, select').on('focusin', function(evt) {
        if (!$('body').data('dirty_initialized')) {
            dirty();
            $('body').data('dirty_initialized', true);
        }
    });
    window.addEventListener('beforeunload', (evt) => {
        if (dirty(true)) {
            if (ifStayFunc) {
                ifStayFunc();
            }
            evt.preventDefault();
            evt.returnValue = '';  // at least Google Chrome requires this
        }
    });
    $('.clearDirty').on('click', function(evt) {
        dirty(null);
    });
};

因此,我将. cleardirty类添加到提供Save的按钮中,这样就可以防止在这种情况下出现警告。 回调ifStayFunc允许我做一些事情,如果用户将停留在页面上,而他是警告。通常我可以显示额外的保存按钮(如果我仍然只看到一些默认/主按钮,这使得安全+SomethingMore -我想允许保存没有这个“SomethingMore”)。

根据之前的回答,并从堆栈溢出的不同地方拼凑在一起,这里是我提出的解决方案,当你实际上想要提交你的更改时,它可以处理这种情况:

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才能不显示警报。

你可以在这里查看详细的解释: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/

主要代码:

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
    }