表单,FormData
概述
表单(<form>)用来收集用户提交的数据,发送到服务器。比如,用户提交用户名和密码,让服务器验证,就要通过表单。表单提供多种控件,让开发者使用,具体的控件种类和用法请参考 HTML 语言的教程。本章主要介绍 JavaScript 与表单的交互。
<form action="/handling-page" method="post">
<div>
<label for="name">用户名:</label>
<input type="text" id="name" name="user_name" autocomplete="username"/>
</div>
<div>
<label for="passwd">密码:</label>
<input type="password" id="passwd" name="user_passwd" autocomplete="current-password" />
</div>
<div>
<input type="submit" id="submit" name="submit_button" value="提交" />
</div>
</form>上面代码就是一个简单的表单,包含三个控件:用户名输入框、密码输入框和提交按钮。
用户点击“提交”按钮,每一个控件都会生成一个键值对,键名是控件的name属性,键值是控件的value属性,键名和键值之间由等号连接。点击type = "submit" 的按钮时,会触发表单提交。所有的键值对都会提交到服务器。
method="post"

method="get"
可以看到,提交时,浏览器会自动对URL进行编码。
注意,表单里面的<button>元素如果没有用type属性指定类型,那么默认就是submit控件。
上面表单的<button>元素,点击以后也会提交表单。
除了点击submit控件提交表单,还可以用表单元素的submit()方法,通过脚本提交表单。
const form = document.querySelector('form')
window.addEventListener('keydown', e => {
console.log(e.key)
if (e.key.toLowerCase === 'enter') {
form.submit()
}
})表单元素的reset()方法和 reset button 可以重置所有控件的值(重置为默认值)。
formElement.reset()<button type="reset">重置</button>
FormData 对象
概述
表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。有时候,我们希望通过 JS 构造和编辑表单键值对,然后自己发送请求。浏览器原生提供了 FormData 对象来完成这项工作。
FormData 首先是一个构造函数,用来生成实例。
var formdata = new FormData(form);
FormData()构造函数的参数是一个表单元素,这个参数是可选的。如果省略参数,就表示一个空的表单,否则就会处理表单元素里面的键值对。
我们用 FormData 对象处理上文提到的表单。
function customSubmit() {
const form = document.querySelector('form')
const data = new FormData(form)
const uname = data.get('user_name')
data.set('user_name', uname.toUpperCase())
fetch('/handling-page', {
method: 'post',
body: data
})
}
window.addEventListener('keydown', e => {
if (e.key.toLowerCase() === 'enter') {
customSubmit()
}
})实例方法
FormData 提供以下实例方法。
FormData.get(key)- `FormData.getAll(key)
FormData.set(key, value)FormData.delete(key)FormData.append(key, value)FormData.has(key)FormData.keys()- `FormData.values()
FormData.entries()
表单的内置验证
自动校验
表单提交的时候,浏览器允许开发者指定一些条件,它会自动验证各个表单控件的值是否符合条件。
<!-- 必填 -->
<input required />
<!-- 必须符合正则表达式 -->
<input pattern="banana|cherry"/>
<!-- 字符串长度必须为6个字符 -->
<input minlength="6" maxlength="6"/>
<!-- 数值必须在1到10之间 -->
<input type="number" min="1" max="10"/>
<!-- 必须填入 Email 地址 -->
<input type="email"/>
<!-- 必须填入 URL -->
<input type="URL"/>如果一个控件通过验证,它就会匹配:valid的 CSS 伪类,浏览器会继续进行表单提交的流程。如果没有通过验证,该控件就会匹配:invalid的 CSS 伪类,浏览器会终止表单提交,并显示一个错误信息。
input:invalid {
border-color: red;
}
input,
input:valid {
border-color: #ccc;
}checkValidity()
除了提交表单的时候,浏览器自动校验表单,还可以手动触发表单的校验。表单元素和表单控件都有checkValidity()方法,用于手动触发校验。
checkValidity()方法返回一个布尔值,true表示通过校验,false表示没有通过校验。因此,提交表单可以封装为下面的函数。
function submitForm(action) {
var form = document.getElementById('form');
form.action = action;
if (form.checkValidity()) {
form.submit();
}
}willValidate 属性
控件元素的willValidate属性是一个布尔值,表示该控件是否会在提交时进行校验。
validationMessage 属性
控件元素的validationMessage属性返回一个字符串,表示控件不满足校验条件时,浏览器显示的提示文本。以下两种情况,该属性返回空字符串。
- 该控件不会在提交时自动校验
- 该控件满足校验条件
setCustomValidity()
控件元素的setCustomValidity()方法用来定制校验失败时的报错信息。它接受一个字符串作为参数,该字符串就是定制的报错信息。如果参数为空字符串,则上次设置的报错信息被清除。
这个方法可以替换浏览器内置的表单验证报错信息,参数就是要显示的报错信息。
var input = document.getElementById('username');
input.oninvalid = function (event) {
event.target.setCustomValidity(
'用户名必须是小写字母,不能为空,最长不超过15个字符'
);
}上面代码中,setCustomValidity()方法是在invalid事件的监听函数里面调用。该方法也可以直接调用,这时如果参数不为空字符串,浏览器就会认为该控件没有通过校验,就会立刻显示该方法设置的报错信息。
validity 属性
控件元素的属性validity属性返回一个ValidityState对象,包含当前校验状态的信息。
该对象有以下属性,全部为只读属性。
ValidityState.badInput:布尔值,表示浏览器是否不能将用户的输入转换成正确的类型,比如用户在数值框里面输入字符串。ValidityState.customError:布尔值,表示是否已经调用setCustomValidity()方法,将校验信息设置为一个非空字符串。ValidityState.patternMismatch:布尔值,表示用户输入的值是否不满足正则模式的要求。ValidityState.rangeOverflow:布尔值,表示用户输入的值是否大于最大范围。ValidityState.rangeUnderflow:布尔值,表示用户输入的值是否小于最小范围。ValidityState.stepMismatch:布尔值,表示用户输入的值不符合步长的设置(即不能被步长值整除)。ValidityState.tooLong:布尔值,表示用户输入的字数超出了最长字数。ValidityState.tooShort:布尔值,表示用户输入的字符少于最短字数。ValidityState.typeMismatch:布尔值,表示用户填入的值不符合类型要求(主要是类型为 Email 或 URL 的情况)。ValidityState.valid:布尔值,表示用户是否满足所有校验条件。ValidityState.valueMissing:布尔值,表示用户没有填入必填的值。
如果想禁止浏览器弹出表单验证的报错信息,可以监听invalid事件, 组织默认事件,并展示自定义错误提示框。
var input = document.getElementById('username');
var form = document.getElementById('form');
var elem = document.createElement('div');
elem.id = 'notify';
elem.style.display = 'none';
form.appendChild(elem);
input.addEventListener('invalid', function (event) {
event.preventDefault();
if (!event.target.validity.valid) {
elem.textContent = '用户名必须是小写字母';
elem.className = 'error';
elem.style.display = 'block';
input.className = 'invalid animated shake';
}
});
input.addEventListener('input', function(event){
if ('block' === elem.style.display) {
input.className = '';
elem.style.display = 'none';
}
});表单的 novalidate 属性
表单元素的 HTML 属性novalidate,可以关闭浏览器的自动校验。这个属性也可以在 JS 里设置。
如果表单元素没有设置novalidate属性,那么提交按钮(<button>或<input>元素)的formnovalidate属性也有同样的作用。
enctype 属性
表单能够用四种编码,向服务器发送数据。编码格式由表单的enctype属性决定。
(1)GET 方法
如果表单使用GET方法发送数据,enctype属性无效。 数据将以 URL 的查询字符串发出。
(2)application/x-www-form-urlencoded
如果表单用POST方法发送数据,并省略enctype属性,那么数据以application/x-www-form-urlencoded格式发送(默认值)。
(3)text/plain
如果表单使用POST方法发送数据,enctype属性为text/plain,那么数据将以纯文本格式发送。
(4)multipart/form-data
如果表单使用POST方法,enctype属性为multipart/form-data,那么数据将以混合的格式发送。
发送的 HTTP 请求如下。
Content-Type: multipart/form-data; boundary=--------314911788813839
------------------------------------314911788813839
Content-Disposition: form-data; name="foo"
bar
------------------------------------314911788813839
Content-Disposition: form-data; name="baz"
The first line.
The second line.
------------------------------------314911788813839--这种格式也是文件上传的格式。
文件上传
用户上传文件,也是通过表单。具体来说,就是通过文件输入框选择本地文件,提交表单的时候,浏览器就会把这个文件发送到服务器。
<input type="file" id="file" name="myFile">
此外,还需要将表单<form>元素的method属性设为POST,enctype属性设为multipart/form-data。其中,enctype属性决定了 HTTP 头信息的Content-Type字段的值,默认情况下这个字段的值是application/x-www-form-urlencoded,但是文件上传的时候要改成multipart/form-data。
<form method="post" enctype="multipart/form-data">
<div>
<label for="file">选择一个文件</label>
<input type="file" id="file" name="myFile" multiple>
</div>
<div>
<input type="submit" id="submit" name="submit_button" value="上传" />
</div>
</form>上面的 HTML 代码中,file 控件的multiple属性,指定可以一次选择多个文件;如果没有这个属性,则一次只能选择一个文件。