正文
表单编码
当用户提交表单时,表单中的数据(每个表单元素的名字和值)编码到一个字符串并随请求发送。默认情况下,HTML 表单通过 POST 方法发送给服务器,而编码后的表单数据则用做请求主体。
对表单数据使用的编码方案相对简单:对每个表单元素的名字和值执行普通的 URL 编码
(使用十六进制转义码替换特殊字符),使用 =
把编码后的名字和值分开,并使用 &
符号分开 名/值对
。
一个简单的表单的编码如下所示:
name=Frankie&age=20&height=180
表单数据编码格式有一个正式的 MIME
类型:
application/x-www-form-urlencoded
当使用 POST 方法提交这种顺序的表单数据市,必须将请求头 Content-Type
设置为该值。
注意:这种类型的编码并不需要 HTML 表单,在 Ajax 应用中,希望发送给服务器的很可能是一个 JavaScript 对象,前面展示的数据变成 JavaScript 对象的表单编码形式可能是:
{ name: 'Frankie', age: 20, height: 180 }
表单编码在 Web 上如此广泛地使用,同时所有服务器端的编程语言都能得到良好的支持,所以非表单数据的表单编码通常也是容易实现的事情,下面代码展示如何实现对象属性的表单编码:
function encodeFormData(data) { if (!data) return '' const pairs = [] // 用于保存名值对 for (let key in data) { if (!data.hasOwnProperty(key) || typeof data[key] === 'function') { continue } let value = data[key].toString() // 把值转换成字符串 let name = encodeURIComponent(key.replace('%20', '+')) // 编码名字 value = encodeURIComponent(value.replace('%20', '+')) // 编码值 pairs.push(name + '=' + value) } return pairs.join('&') // 返回使用'&'连接的名值对 } const data = { name: '越前君', age: 20, height: 180 } console.log(encodeFormData(data)) // name=%E8%B6%8A%E5%89%8D%E5%90%9B&age=20&height=180
表单序列化
随着 Ajax 的出现,表单序列化已经成为一种常见的需求,在 JavaScript 中,可以利用表单字段的 type
属性,连同 name
和 value
属性一起实现对表单序列化。在编写代码之前,有必要先弄清楚在表单提交期间,浏览器是如何将数据发送给服务器的。
- 对表单字段的名称和值进行
URL 编码
,使用&
分隔; - 不发送禁用的表单字段;
- 只发送勾选的复选框和单选框按钮;
- 不发送
type
为reset
和button
的按钮; - 对选选择框中,每个选中的值单独一个条目;
- 在单击提交按钮提交表单的情况下,也会发送提交按钮;否则,不发送提价按钮。也包括
type
为image
的<input>
元素; <select>
元素的值,就是选中的<option>
元素的value
属性值。 如果<option>
元素没有value
属性,则是<option>
元素的文本值
在表单序列化过程中,一般不包括任何按钮字段,因为结果字符串很可能是通过其他方式提交的。除此之外的其他上述规则都应该遵循。
在下面表单序列化 serialize()
函数中,首先定义了一个名为 parts
数组,用于保存将要创建的字符串的各个部分。然后,通过 for
循环遍历每个表单字段,并将其保存在 field
变量中。在活动一个字段的引用之后,使用 switch
语句检测其 type
属性。
序列化过程中最麻烦的就是 <select>
元素,它可能是单选框,也可能是多选框。为此,需要遍历控件中的每一个选项,并在相应选项被选中的情况下向数组添加一个值。对于单选框,只有一个选中项,而多选框则可能有零个或多个选中项。这里的代码适用于这两种选择框,至于可选项的数量则是由浏览器控制的。在找到一个选中项之后,需要确定使用什么值。如果不存在 value
属性,或者虽然存在该属性,但值为空字符串,都要使用选项的文本来代替。为检查这个特性,在 DOM 兼容的浏览器中需要使用 hasAttribute()
方法,而在 IE7 中需要使用属性 specified
属性。
如果表单中包含 <fieldset>
元素,则该元素会出现在元素集合中,但没有 type
属性。因此,如果 type
属性未定义,则不需要对其进行序列化。同样,对于各种按钮以及文件输入字段也是如此(文件输入字段在表单提交过程中包含文件的内容;但是,这个字段是无法模仿的,序列化时一般都要忽略)
对于单选按钮和复选框,要检查其 checked
属性是否被设置为 false
,如果是则退出 switch
语句。如果 checked
属性是 true
,则继续执行 default
语句,即将当前字段的名称和值进行编码,然后添加到 parts
数组中。函数的最后一步,就是使用 join()
个数整个字符串,也就是用 &
来分隔每一个表单字段。
最后 serialize()
函数会以查询字符串的格式输出序列化之后的字符串。
// 未完待续,例子晚点加上