查找 HTML 表单是否已更改。

21

我使用jquery给表单添加了一个change事件处理程序。当任何输入框被更改时,它都能正常工作。但是,如果某些其他代码更改输入框的值,它就不能正常工作。

是否有办法检测表单是否已更改,即使其输入框的值已经被代码更改了?

7个回答

40

是的,关于这个问题似乎存在一些混淆。在理想情况下,您期望当输入内容发生更改时,onchange事件会发生,但事实并非如此。我相信这样做有充分的理由 - 也许不是。

我克服这个障碍的方法之一是将表单状态捕获到变量中,在显示表单后和提交表单前检查状态是否已更改,并根据情况采取行动。

一个易于存储的状态是serialize函数返回的内容。一个方便的地方来存储该状态是使用data功能。jquery提供了serializedata两种方式。

当然,您可以使用其他不同形式的状态(某种哈希)或存储此状态的存储器(例如标准全局变量)。

这是一些原型代码:

如果您的表单id为“xform”,则可以在表单显示时调用以下代码:

$('#xform').data('serialize',$('#xform').serialize());

然后,当你需要检查时,例如在按钮提交之前,你可以使用以下代码:

if($('#xform').serialize()!=$('#xform').data('serialize')){
    // Form has changed!!!
}

你可以将所有这些内容封装成一个复制并粘贴的JavaScript代码片段,该代码片段将为您提供一个formHasChanged()函数,您可以在需要时调用它(未经测试):

你可以把这一切都整合到一个复制并粘贴的 JavaScript 代码片段中,这样就可以得到一个名为formHasChanged()的函数,并且可以在需要的地方调用它(未经测试):

$(function() {
    $('#xform').data('serialize',$('#xform').serialize());
});
function formHasChanged(){
    if($('#xform').serialize()!=$('#xform').data('serialize')){
        return(true);
    }
    return(false);
}

但是如果继续下去,我会再次创建另一个jQuery插件。


它在很多情况下都有效,但我遇到了一些字段在某些情况下被禁用的情况,当发生这种情况时,它们不在表单的序列化值中,系统在比较两个字符串时检测到变化,而实际上并没有变化。为了在这种情况下正常工作,必须确保在获取序列化函数的结果之前设置控件状态。无论如何,这是一个非常好的解决方案! :) +1 - Samuel
我认为这个解决方案https://dev59.com/x3NA5IYBdhLWcg3wcNbF更好,因为它可以处理禁用字段。 - Samuel
避免在2018年及以后使用jQuery。一个更高效的算法,不依赖于哈希/序列化函数,可以在我的下面的答案中找到。 - anthumchris
3
@AnthumChris 嗯,有些人可能会跟你争辩这一点,但我肯定会感谢你的投反对票。 - zaf
对于非jQuery,请参考HTMLElement.dataSet.data(),但是原生)和传递给URLSearchParamsFormData(序列化,但是原生)。现在没有任何借口使用Internet Explorer 6了。抱歉,我是指jQuery。因为IE6是为其而补偿的。 - amcgregor
对于非jQuery,请参考HTMLElement.dataSet.data(),但是原生的)和通过URLSearchParams传递给FormData(序列化,但是原生的)。实际答案也被投票降低了。现在没有使用Internet Explorer 6的借口了(https://gist.github.com/amcgregor/d6ea618b772b52bb288cff72c8381640#jquery)。抱歉,我是指jQuery。因为IE6是它的补偿。 - undefined

10

将表单序列化肯定是一种选择,但如果:

  • 你想知道哪些字段已更改
  • 只需要检查字段的子集
  • 动态添加或删除字段

幸运的是,每个表单元素都有与其对象相关联的默认值:

  • input、textarea : defaultValue
  • checkbox、radio : defaultChecked
  • select: defaultSelected

例如:要检查输入框或文本区域是否已更改,请使用:

var changed = false;
$(":text,textarea").each(function(){
    changed = this.value != this.defaultValue;
    return !changed; // return if at least one control has changed value
});

"defaultValue" 属性似乎已经过时:链接 - Heisenberg

5

在JavaScript中,可以轻松实现此操作而无需使用jQuery。initChangeDetection()可以被多次调用:

function initChangeDetection(form) {
  Array.from(form).forEach(el => el.dataset.origValue = el.value);
}
function formHasChanges(form) {
  return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value);
}

在JS Bin上进行测试


对于不支持新的箭头/数组函数的旧浏览器:

function initChangeDetection(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    el.dataset.origValue = el.value;
  }
}
function formHasChanges(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    if ('origValue' in el.dataset && el.dataset.origValue !== el.value) {
      return true;
    }
  }
  return false;
}

superp! merci merci - Justin
5
.value 无法告诉你一个 checkboxradio 输入是否已更改。在这些情况下,需要使用 .checked - xr280xr

1

不是常规的方式。

您可以通过更改输入并触发更改事件来进行更改。

$('#inputId').val('foo').trigger('change');

或者使用这个:

$('#inputId').val('foo').change();

2
很好的回答!但谁给了负评,我想他/她完全不知道trigger('change')change() - thecodeparadox
2
@thecodeparadox。太多的傻瓜有键盘和鼠标... :) - gdoron
但是他们不知道在哪里使用它们。 - thecodeparadox
我没有点踩,但我认为DOMAttrModified不会在任何情况下触发输入字段的值改变;value是一个属性而不是DOM节点... https://dev59.com/l0XRa4cB1Zd3GeqPq1d8#147011 - 此外,OP想知道是否有东西改变了输入,那可能是扫描仪或类似设备,这意味着他没有在任何地方编写更改的脚本。 - mplungjan
@mplungjan。我后来添加了突变事件。无论如何,你是正确的,我正在删除那部分内容。 - gdoron

0
使用原生JavaScript,您可以对输入的值进行哈希处理。
您可以对表单中的值进行哈希处理。我建议只使用每个输入的名称作为键,值作为值。然后,您可以使用查询选择器来循环遍历表单中的每个输入,以获取哈希值。
要检查表单中的值是否发生了变化,您只需比较表单的哈希值即可。我将初始哈希存储在表单的数据属性中。
以下是一种可能的实现方式:
function initFormHash(form) {
    form.setAttribute("data-initial-hash", getFormHash(form));
}

function getFormHash(form) {
    const formValues = {};

    form.querySelectorAll("input").forEach(input => {
        const inputType = input.getAttribute("type");
        const inputName = input.getAttribute("name");

        if (inputType === "hidden") return;
        if (!inputName) return;

        if (inputType === "checkbox" || inputType == "radio") {
            formValues[inputName] = input.checked;
            return;
        }

        formValues[inputName] = input.value;
    });

    form.querySelectorAll("textarea, select").forEach(textarea => {
        const name = textarea.getAttribute("name");

        if (!name) return;

        formValues[name] = textarea.value;
    })

    return JSON.stringify(formValues);
}

function formChanged(form) {
    return (form.getAttribute("data-initial-hash") !== getFormHash(form));
}

0

这是我做的(我找到了我的解决方案,使用了zaf的答案)

$("form").change(function() {
    $(this).data("changed","true");
});

$("input[type='submit']").click(function() {
    if($("form").data("changed") == "true") {
        var discard = confirm("Some unsaved changes. Discard them ?");
        if(!discard) return false;
    }
});

你可以使用 return confirm("..."); 语句,但我认为那样会更难阅读。 - Xeltor

-1
尝试使用onchange属性,根据W3C的文档,它应该在元素内容、选择或选中状态发生改变时触发。

这是错误的,它不会随着代码更改而起作用。哦,而且更改处理程序就像使用onchange属性一样,但是分离关注点。 - gdoron
@gdoron jQuery.trigger()或jQuery.change()使用什么来检测代码更改? - Stefan Rogin
它只是触发了 onchange 处理程序。 - gdoron

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