序列化表单数据

在前公司的项目里,遇到过一个需求,需要将整个表单的数据提出来序列化成字符串,然后使用AJAX来
提交数据,
当时凭着经验写了一个,工作的还不错,没有出现过问题,现在想来,当时写的很不严谨,我缺少了很
多验证和测试的步骤,最明显的一点是,我甚至说不清到底哪些表单控件的值才是真正会被提交的>_<.

所以一个详细的测试是不可缺少的,我写了一个放置了所有表单元素的页面,使用fiddler监控了提交的数据,测试结果如下:

注:在这个测试中,我并没有选择文件进行上传,而是直接点击了submit按钮。

从图中我们可以看到浏览器们只对两个地方存在分歧:

1.只有 IE6,IE7 都会提交<button></button>的value
2.而Chrome则没有提交<input type="file" />

第2个分歧在进一步测试时被纠正,Chrome的处理方式是,如果你不选择文件,那么就不提交file域的
名字和值,否则才会提交.
而第1个则非常奇怪,其他浏览器似乎都无视了<button>,只有ie给于了关注,而通过查资料,我得到
更多的信息:

当 BUTTON 元素在表单中提交的话,Microsoft® Internet Explorer 5 及以后版本将提交 VALUE 标签
属性,
若存在的话。否则就提交 innerText 属性。在 Internet Explorer 4.0 中,只会提交 innerText 值
(这个结果非常有趣,所以你的button最好别加name属性,否则可能会提交很多数据)

知道了具体会上传啥东西,本着节省传输数据量的精神,我们就可以做个总结。


首先button是要被干掉的,因为其他浏览器均不支持,放上去也只是浪费字节
file域我们可以采用Chrome的处理方式,只有选择文件后才会被提交,但是AJAX无法上传文件,所以实际上file域没有意义,可以不提交
radio组和checkbox组同上,一组中有一个(或多个)被选中的值会被提交,如果一个也没选中,都不
会被提交
select只提交选中的值,如果没有一个选中的,那么提交第一项
select multiple 只提交选中的值,如果没有一个选中的,那么不会被提交
其余的控件不管有没有值都会被提交


剩下需要的提交的就是
<input type="text" />
<input type="password" />
<input type="hidden" />
<input type="submit" />

<input type="radio" /> //选择选中的
<input type="checkbox" />//选择选中的
<input type="file" />

<select></select>
<select multiple></select>
<textarea></textarea>

最后就是需要给所有的值做转义,有能力给这个表单序列化结果附加更多的数据

我的实现如下:

 代码

 1 function FormDataSerialize( form /*HTMLFormElement*/,attachParams /* Object */ )
 2 
 3     var encode = encodeURIComponent, 
 4     currCon, 
 5     currTag, 
 6     currVal,    
 7     currType, 
 8     currName,    
 9     currOpts, 
10     currOpt,
11     temp,
12     result   = [], 
13     els      = form.elements, 
14     iTypeReg = /^checkbox|radio$/i, 
15     bTypeReg = /^button|reset|image$/i,
16     A = Array;
17 
18     forvar l = els.length; l--;  )
19     {
20         currCon   = els[ l ];
21         currTag   = currCon.tagName.toLowerCase();
22         currType  = currCon.type;
23         currVal   = currCon.value;
24         currName  = currCon.name;    
25         
26         if( currTag == 'button' ||   //如果是button,忽略掉                 
27             ( iTypeReg.test( currType ) && currCon.checked === false )  || //如果是radio或checkbox,而且checked为false,忽略掉
28             ( bTypeReg.test( currType ) || //如果是type=button,type=reset,type=image的控件,忽略掉
29             ( currType == 'file' ) )  //如果是file域 ,忽略掉            
30         ) continue;  
31 
32         if( currCon.multiple === true && currType == 'select-multiple' ) //如果是多选框,遍历options特殊处理
33         {
34             currOpts = currCon.options;
35             forvar m = currOpts.length; m--;  )
36                 ( currOpt = currOpts[m] ).selected && 
37                 ( result.push( encode( currName ) + '=' + encode( currOpt.value ) ) );
38             continue;
39         }
40 
41         //其余的只需要取value就好
42         result.push( encode( currName ) + '=' + encode( currVal ) );
43     }
44     
45     //处理附加的数据
46     forvar item in attachParams )
47     { 
48         if( ( temp = attachParams[ item ] ) instanceof A )  //数组使用多选框处理方式
49             for( l = temp.length; l-- ; ) result.push( encode( item ) + '=' + encode( temp[l] ) );
50         else result.push( encode( item ) + '=' + encode( temp ) ); //其余直接取key=value就好
51     } 
52     
53     //最后加上分隔符的&
54     return result.join('&');
55 }

 

 

 使用方式:

1 var dataStr = FormDataSerialize( document.getElementById( 'myForm' ),
2 {
3     username : 'wait',
4     password : '123456789',
5     keys     : [ 1,2,3,4,5 ]
6 });

 

 


OK,函数基本上就是这个样子了,还有有些可以改进的地方,比如 attachParams 可以接受string类型的。

上面也说了,这个函数一般都用在AJAX提交数据上。显然这个需求很旺盛,于是Firefox在版本4内置了一个 FormData 对象.

它4这样子用滴:

代码
1 var formData = new FormData();
2 formData.append("username""Groucho");
3 formData.append("accountnum"123456);
4 formData.append("afile", fileInputElement.files[0]);
5 
6 xhr.open("POST""http://foo.com/submitform.php");
7 xhr.send(formData);

 

 不但可以无中生有,还可以直接从form对象导出FormData对象:

1 var formElement = document.getElementById("myFormElement");
2 formData = formElement.getFormData();
3 formData.append("serialnumber", serialNumber++);
4 xhr.send(formData);

 

 看起来真的很好用蛤,但是,比较恶心银的是它不会被转换成字符串,formData本身就是一个对象,也只能用在AJAX的send方法里。


FormData参考:

https://developer.mozilla.org/en/XMLHttpRequest/FormData

https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects

 

 2009/7/9 update:

今天对FormData做了一些更详细的测试,原来FromData并不是像我想象的那样, 它的存在主要是为了解决上传文件的问题,也就是说,

如果你的表单中有许多控件,包括文件域,这时候你想同时用AJAX提交这些混合内容, 就可以使用FormData了(可以用AJAX上传文件已经是一个惊喜了)。

 

FormData 要求表单必须使用 method="post" enctype="multipart/form-data"  (GET是无效的,什么也不会被传递)

而且FormData传递的数据也完全跟表单正常post一模一样,类似如下:

 

-----------------------------29962268197975

Content-Disposition: form-data; name="input-text"

text-value

-----------------------------29962268197975

Content-Disposition: form-data; name="input-hidden"

hidden-value

-----------------------------29962268197975

Content-Disposition: form-data; name="input-password"

password

-----------------------------29962268197975

Content-Disposition: form-data; name="input-radio-group"

radio-group-4

  

看来是我高兴早了,FormData是没有办法完全代替FormDataSerialize函数的功能的。

 

 

posted @ 2010-07-09 00:13  waitcat  阅读(1381)  评论(4编辑  收藏  举报