如何最简单地检测HTML表单中是否至少更改了一个字段?

45

我有一个包含20多个字段的HTML表单。页面上还有一些链接,可能会在没有保存任何更改的情况下将用户导向其他页面。

当用户点击这些链接时,我想通过JS确认警告用户,如果其中任何一个表单字段已更改,但我不想创建一个庞大的开关语句,然后需要维护它来添加新字段到表单中。我知道如何在Javascript中创建长列表的“if”语句,命名每个字段并检查每个值,但如果可以避免,我不想这样做。

最简单的方法是如何检查用户是否更改了至少一个字段值?

8个回答

72

做法

  1. 在显示表单之前(使用jQuery方式Prototype方式),对表单进行序列化(包括其所有值)。
  2. "onbeforeunload" 事件处理程序中再次序列化。

如果两者不匹配,则必须更改了表单,因此从您的 onbeforeunload 处理程序返回字符串(例如 "您有未保存的数据")。

这种方法允许表单字段发展,同时“确认是否更改”逻辑保持不变。

示例 (混合javascript和jquery)

var form_clean;

// serialize clean form
$(function() { 
    form_clean = $("form").serialize();  
});

// compare clean and dirty form before leaving
window.onbeforeunload = function (e) {
    var form_dirty = $("form").serialize();
    if(form_clean != form_dirty) {
        return 'There is unsaved form data.';
    }
};

哦,那是一个非常棒的想法。不错。 - Brian MacKay
13
2014年,这仍然是最佳实践吗? - Kevin Meredith
2
第一行不仅仅是 var form_clean = $("form").serialize(); 吗? - Edd
这在动态添加HTML元素时如何工作?似乎您需要时刻注意任何更改DOM的操作,并重新调用序列化方法。此外,如果使用像Knockout和Angular这样的库,您甚至不需要将表单值放在表单内,那么它如何工作呢? - SventoryMang

13

我非常确定这是一个不好的想法,但我想提出来。

表单字段有一种获取“默认值”的方法(即加载字段时该字段具有的值),您可以将其与当前值进行比较。简单地循环所有字段可消除添加表单字段时需要维护的麻烦。

可能会存在与“默认值”属性相关的各种浏览器错误,因此如果没有经过广泛测试,我不会信任此方法。下面的代码是概念验证,并未在任何实际应用程序中使用(由我)。

function IsDirty(form) {
    for (var i=0; i<form.elements.length; i++) {
        var field = form.elements[i];
        switch (field.type) {
            case "select-multiple":
            case "select-one":
                var options = field.options;
                for (var j=0; j<options.length; j++) {
                    if(options[j].selected != options[j].defaultSelected) return true;
                }
                break;
            case "text":
            case "file":
            case "password":
                if (field.value != field.defaultValue) return true;
                break;
            case "checkbox":
            case "radio":
                if (field.checked != field.defaultChecked) return true;
                break;
        }
    }
    return false;
}

据我所知,这是唯一的方法。它可以防止出现棘手的问题,例如在刷新后 Firefox 保留了新表单值。 - Bob Jansen

9
使用jQuery非常容易。您应该能够使用相同的方法在原生JavaScript中实现相同的结果。
var $inps = $('#myForm').find('input,select,textarea')
  , formAltered = false
;
$inps.change(function() {
    formAltered = true;
    $inps.unbind('change'); // saves this function running every time.
});

唯一的问题是,如果你更改了一个值,然后将其改回原始值,它仍会报告表单已更改。


+1,您可以详细说明一下如何消除数据实际上未更改的情况 - 您可以保存初始状态并将其与之进行比较(使用onfocus事件),然后才将表单设置为“dirty”。 - Dror
你可以将第一行改为:var $inps = $('#myForm :input'), formAltered = false; - ANeves

6

以下是您可以添加到表单中的一行代码:

$(':input',document.myForm).bind("change", function() { 
  enablePrompt(true); }); // Prevent accidental navigation away

然后,您可以为整个网站创建enableUnloadPrompt()函数:

function enablePrompt(enabled) {
  window.onbeforeunload = enabled ? "Your changes are not saved!" : null;
}

最后,在正确提交表单之前,请确保:
- 所有必填字段都已填写。 - 所有输入数据都符合格式要求。 - 检查所有输入数据是否准确无误。
请注意,提交表单后将无法更改已输入的数据。
enablePrompt(false);

这将不会检查表单的值是否不同,只是检查用户是否曾经更改过表单。但是,它非常简单易用。


3
这可以通过一个布尔变量来处理,我们称之为脏位处理。通常在网页中,一旦用户对任何字段执行了某些编辑操作,表单就被视为已更改(即使在编辑后数据保持不变)。当用户试图离开页面时,会提示用户是否要保存更改。
按照标准做法,没有检查编辑某个字段后该值是否实际上已更改。例如:如果用户编辑并将“xyz”附加到文本字段,然后删除“xyz”,则表单数据仍然与编辑前相同,但表单仍然被视为“脏”,当用户尝试导航离开时会提示警告消息。
因此,如果您想要实现这个功能,事情变得非常简单。您只需要将 onchange() 事件处理程序添加到控件中,并在这些事件处理程序中将全局布尔变量(例如 isDirty)设置为 true。
一旦用户想要导航离开,您可以闪现一条消息“当前页面可能有未保存的更改。您是否希望保存它们?”即使用户注意到他的编辑并没有改变初始数据,也不会感到失望。
上面给出的答案实现了这种行为。我写这篇文章是因为您似乎有一个想法,即检查每个字段的初始值以查看是否真的在编辑后更改了它。只是想告诉您,根本不需要检查每个字段。

0
在我的情况下,我结合了@Crescent Fresh和@nickf的答案,并得到了这个答案:
var formBefore;

var $inps = $('form').find('input,select,textarea');

$(function() {
    formBefore = $("form").serialize();
});

$inps.change(function () {
    var changedForm = $("form").serialize();
    if (formBefore != changedForm) {
        $("#btnSave").css('background-color', 'green');
    } else {
        $("#btnSave").css('background-color', '');
    }
});

我认为你最好把这个放在第三行: $("#btnSave").css('background-color', ''); - andymnc
我知道这是几年后的事情,但仍然可能有用。虽然颜色不同,但按钮仍然有效,所以最好像这样:let formBefore; let $inps = $('form :input'); $("#btnSave").prop("disabled", true ); $(function() { formBefore = $("form").serialize(); }); $inps.change(function () { var changedForm = $("form").serialize(); if (formBefore != changedForm) { $("#btnSave").prop("disabled", false); } else { $("#btnSave").css('background-color', ''); $("#btnSave").prop("disabled", true ); } }); - andymnc

0

之前发布的答案经过一些小改进后已经在这些年中证明了自己。这是我在onBeforeUnload事件函数中使用的函数。

/*
** 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} true if the form is dirty, false otherwise.
*/
function formIsDirty(form) {
    for (var i = 0; i < form.elements.length; i++) {
        var element = form.elements[i];
        var type = element.type;
        switch (element.type) {
        case "checkbox":
        case "radio":
            if (element.checked != element.defaultChecked)
                return true;
            break;
        case "number":
        case "hidden":
        case "password":
        case "date":
        case "text":
        case "textarea":
            if (element.value != element.defaultValue)
                return true;
            break;
        case "select-one":
        case "select-multiple":
            for (var j = 0; j < element.options.length; j++)
                if (element.options[j].selected != element.options[j].defaultSelected)
                    return true;
            break;
        }
    }
    return false;
}

这里是一个onBeforeUnload处理函数的示例:

    function onBeforeUnload(event) {
        event = event || window.event;
        for (i = 0; i < document.forms.length; i++) {
            switch (document.forms[i].id) {
            case "search":
                break;
            default:
                if (formIsDirty(document.forms[i])) {
                    if (event)
                        event.returnValue = "You have unsaved changes.";
                    return "You have unsaved changes.";
                }
                break;
            }
        }
    }

0

<form>标签具有onChange回调函数,每当任何字段触发onChange事件时都会触发。

示例:

<form onChange={isDirtyFunc}>
   <input name="name" type="text"/>
   <input name="address" type="text"/>
   <input name="age" type="number"/>
</form>

如果任何字段发生更改,则使用相关事件调用isDirtyFunc函数。


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