FormData() 对象不会从表单添加提交类型的输入,而在 Firefox 上

Posted

技术标签:

【中文标题】FormData() 对象不会从表单添加提交类型的输入,而在 Firefox 上【英文标题】:FormData() object does not add submit-type inputs from form, while on Firefox 【发布时间】:2016-11-11 16:28:18 【问题描述】:

今天我遇到了一个有趣的错误,花了很多时间才弄清楚。

设置

页面上的表单。提交时,数据被捕获并用它创建new FormData() 对象。

该对象通过 xhr 请求发送到 .php 脚本,然后返回 ok / error 消息。

代码看起来像这样:(简化版,不需要绒毛

<form name="frm" id="frm" action="" method="post" onsubmit="save(event, this);" enctype="multipart/form-data">
    <input name="name" id="name" type="text" value="..." />
    <input name="email" id="email" type="text" value="..." />
    <input name="phone" id="phone" type="text" value="..." />
    <input name="website" id="website" type="text" value="..." />
    <textarea name="details" id="details"></textarea>
    <input name="send" type="submit" value="Send" />
</form>

<script type="text/javascript">

function save(e, frm) 

        if (document.getElementById('nume').value == '' ||
          document.getElementById('email').value == '' ||
          document.getElementById('telefon').value == '' ||
          document.getElementById('site').value == '') 

            alert('Forms empty');

         else 

            var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');

            xhr.onreadystatechange = function () 
                if (xhr.readyState == 4) 

                    var r = JSON.parse(xhr.responseText);

                    if (r.code == 0) 
                        document.getElementById('message_ok').style.display = 'block';
                     else 
                        document.getElementById('message_err').style.display = 'block';
                    
                
            ;

            xhr.open('POST', 'http://url', true);
            var data = new FormData(frm);
            xhr.send(data);

        
    e.preventDefault();


</script>

将此发送到.php 将生成一个如下所示的数组:

Array
(
    [name] => some name
    [email] => some email
    [phone] => 11111111
    [website] => some site
    [details] => some details
    [send] => Send
)

.php 将回复"message":"ok","code":0"message":"error","code":1

现在这是预期的行为。这是我在 Chrome、IE 或 Safari 上得到的。

问题

然而,在 Firefox 上,我得到相同的数组,但没有 submit 输入 (name="send") 键/值对:

Array
(
    [name] => some name
    [email] => some email
    [phone] => 11111111
    [website] => some site
    [details] => some details
)

我在 Linux 和 Windows 上都试过了,以覆盖我的基础,但它仍然给出了同样不令人满意的结果。

解决方案

在网上搜索并出现空白后,我解决它的方法(更多的补丁,而不是真正解决)是覆盖send键/值:

var data = new FormData(frm);
data.append('send', 'Send');
xhr.send(data);

这是可行的,因为如果它已经定义(Chrome 等......),它会被覆盖,如果它不存在,它就会被创建。

问题

    类似的 - 你有没有遇到过类似的事情? 修复 - 我认为我的解决方案是 hack,您有什么更好的修复方法吗?

【问题讨论】:

这是预期的行为。提交按钮只有在表单正常提交时才会自动发送。当您在 Javascript 中创建 FormData 时,它不知道提交与特定的提交按钮相关,因此它无法将其自动添加到 FormData。我很惊讶它在其他浏览器中的工作方式不同。 很抱歉,我不同意你的观点。文档中没有任何地方这么说。你可能是对的,但在我看到实际证据之前,我仍然相信这不是预期的行为。请参阅:developer.mozilla.org/en-US/docs/Web/API/FormData 和 developer.mozilla.org/en-US/docs/Web/API/FormData/… 我刚刚看到 Chrome 58 出现这种行为的页面,而使用 Chrome 53 的同事“很好”。也就是说,从表单创建了一个新的 FormData 对象,而我的不包括提交者,而他的包含。非常令人沮丧,因为表单上有几个提交按钮,它们有自己的名称和值。 【参考方案1】:

根据 WHATWG 规范,FireFox 似乎是正确的。

FormData 构造函数的 XMLHttpRequest 规范说:

    如果给定了form,则将fd 的条目设置为constructing the form data set 的结果form

那么在构造表单数据集的描述中,是这样说的:

在提交者submitter 的上下文中form 可选地构造表单数据集 的算法如下。如果没有另外指定,提交者为空。

表单中的按钮只有在提交者的情况下才会包含在表单数据集中。但是当这个算法从FormData构造函数中执行时,没有指定提交者,所以表单数据集中不应该包含任何按钮。

【讨论】:

过失!我猜你一直都是对的。感谢您提供规格的链接,因此我可以提供一些可靠的证据。附带说明:令人恐惧的是,有多少浏览器实现了不符合规范的功能。 即使在submit 处理程序中调用了FormData,Chrome 似乎也错过了按钮。在这种情况下,event.submitter 确实包含对正确按钮的引用,但 Chrome 仍然没有将其包含在 FormData 中。 这是预期的行为。 FormData 构造函数没有接收event 作为参数,所以它不知道event.submitter 是什么。该按钮仅包含在默认的表单提交代码中。【参考方案2】:

在 Chrome 58 中遇到类似情况。

在我的问题中,有多个提交按钮,我需要知道选择了哪一个,或者至少知道是否选择了一个特定的按钮。

我可怕的 hack 是显式监听该按钮被选中,创建一个隐藏的输入并将其插入到提交按钮之前。

更新:改进的黑客攻击

监听提交按钮的点击,并通过按钮作为提交者发送到 on('submit'),并在 ajax 调用中使用之前附加到 FormData 对象。

根据输入[type="submit"] 进行修改,并可能对submitter 的内容进行额外的错误检查。

$('#defaultModalObject-1').on('click', 'button[type="submit"]', function(event) 
    /* horrible hack to detect wizard_goto_step submissions via ajax */
    event.preventDefault();
    $(event.target.form).trigger('submit', event.target);
);

$('#defaultModalObject-1').on('submit', 'form', function(event, submitter)
    ...
    var formdata = new FormData(form[0]);

    if (submitter != undefined) 
        formdata.append(submitter.name, submitter.value);
    
    ...
);

原始黑客

$('#ajax_form_modal_result').on('click', 'button[type="submit"][name="wizard_goto_step"]', function(event) 
    /* horrible hack to detect wizard_goto_step submissions via ajax */
    event.preventDefault();
    var input = $('input[type="hidden"]').attr('name', 'wizard_goto_step').val(event.target.value);
    $(event.target).before(input);
    $(event.target.form).trigger('submit');
);

很遗憾,我的委托表单处理程序 $('#defaultModalObject-1').on('submit', 'form', function(event) ... ); 上没有创建者信息

【讨论】:

哪个,顺便说一句,似乎仍然不起作用。当我得到一些工作代码时,我会编辑我的答案。【参考方案3】:

我们在 linux 客户端上遇到同样的问题,使用下面的代码将所有按钮元素添加到我们的表单数据中。

第一个问题是在某些代码中获取表单数据时出错

if($this.is('form'))

  var fd = new FormData($this.get(0));

else

  var fd = new FormData();

第二个问题类似于以 ajax 形式添加按钮的问题。有时我们在一个表单中有多个按钮,并且想要检测按下了哪个按钮。

$this.find('button').each(function()

  fd.append(this.getAttribute('name'), this.value);
);

如果有人需要使用输入类型提交可以添加上面的代码,我们在这种情况下使用按钮:)

【讨论】:

以上是关于FormData() 对象不会从表单添加提交类型的输入,而在 Firefox 上的主要内容,如果未能解决你的问题,请参考以下文章

从javascript函数创建的表单提交FormData到php

使用ajax提交form表单

axios怎么模拟form表单提交,怎么传递FormData对象

axios用表单形式提交数据

FormData、Blob、Map等几个对象概念

FormData对象