JavaScript-浏览器模型(五)
4.2 URLSearchParams.toString()
toString
方法返回实例的字符串形式。
var url = new URL('https://example.com?foo=1&bar=2');
var params = new URLSearchParams(url.search);
params.toString() // "foo=1&bar=2'
那么需要字符串的场合,会自动调用toString
方法。
var params = new URLSearchParams({version: 2.0});
window.location.href = location.pathname + '?' + params;
上面代码中,location.href
赋值时,可以直接使用params
对象。这时就会自动调用toString
方法。
4.3 URLSearchParams.append()
append()
方法用来追加一个查询参数。它接受两个参数,第一个为键名,第二个为键值,没有返回值。
var params = new URLSearchParams({'foo': 1 , 'bar': 2});
params.append('baz', 3);
params.toString() // "foo=1&bar=2&baz=3"
append()
方法不会识别是否键名已经存在。
var params = new URLSearchParams({'foo': 1 , 'bar': 2});
params.append('foo', 3);
params.toString() // "foo=1&bar=2&foo=3"
上面代码中,查询字符串里面foo
已经存在了,但是append
依然会追加一个同名键。
4.4 URLSearchParams.delete()
delete()
方法用来删除指定的查询参数。它接受键名作为参数。
var params = new URLSearchParams({'foo': 1 , 'bar': 2});
params.delete('bar');
params.toString() // "foo=1"
4.5 URLSearchParams.has()
has()
方法返回一个布尔值,表示查询字符串是否包含指定的键名。
var params = new URLSearchParams({'foo': 1 , 'bar': 2});
params.has('bar') // true
params.has('baz') // false
4.6 URLSearchParams.set()
set()
方法用来设置查询字符串的键值。
它接受两个参数,第一个是键名,第二个是键值。如果是已经存在的键,键值会被改写,否则会被追加。
var params = new URLSearchParams('?foo=1');
params.set('foo', 2);
params.toString() // "foo=2"
params.set('bar', 3);
params.toString() // "foo=2&bar=3"
上面代码中,foo
是已经存在的键,bar
是还不存在的键。
如果有多个的同名键,set
会移除现存所有的键。
var params = new URLSearchParams('?foo=1&foo=2');
params.set('foo', 3);
params.toString() // "foo=3"
下面是一个替换当前 URL 的例子。
// URL: https://example.com?version=1.0
var params = new URLSearchParams(location.search.slice(1));
params.set('version', 2.0);
window.history.replaceState({}, '', location.pathname + `?` + params);
// URL: https://example.com?version=2.0
4.7 URLSearchParams.get(),URLSearchParams.getAll()
get()
方法用来读取查询字符串里面的指定键。它接受键名作为参数。
var params = new URLSearchParams('?foo=1');
params.get('foo') // "1"
params.get('bar') // null
两个地方需要注意。第一,它返回的是字符串,如果原始值是数值,需要转一下类型;第二,如果指定的键名不存在,返回值是null
。
如果有多个的同名键,get
返回位置最前面的那个键值。
var params = new URLSearchParams('?foo=3&foo=2&foo=1');
params.get('foo') // "3"
上面代码中,查询字符串有三个foo
键,get
方法返回最前面的键值3
。
getAll()
方法返回一个数组,成员是指定键的所有键值。它接受键名作为参数。
var params = new URLSearchParams('?foo=1&foo=2');
params.getAll('foo') // ["1", "2"]
上面代码中,查询字符串有两个foo
键,getAll
返回的数组就有两个成员。
4.8 URLSearchParams.sort()
sort()
方法对查询字符串里面的键进行排序,规则是按照 Unicode 码点从小到大排列。
该方法没有返回值,或者说返回值是undefined
。
var params = new URLSearchParams('c=4&a=2&b=3&a=1');
params.sort();
params.toString() // "a=2&a=1&b=3&c=4"
上面代码中,如果有两个同名的键a
,它们之间不会排序,而是保留原始的顺序。
4.9 URLSearchParams.keys(),URLSearchParams.values(),URLSearchParams.entries()
这三个方法都返回一个遍历器对象,供for...of
循环遍历。它们的区别在于,keys
方法返回的是键名的遍历器,values
方法返回的是键值的遍历器,entries
返回的是键值对的遍历器。
var params = new URLSearchParams('a=1&b=2');
for(var p of params.keys()) {
console.log(p);
}
// a
// b
for(var p of params.values()) {
console.log(p);
}
// 1
// 2
for(var p of params.entries()) {
console.log(p);
}
// ["a", "1"]
// ["b", "2"]
如果直接对URLSearchParams
进行遍历,其实内部调用的就是entries
接口。
for (var p of params) {}
// 等同于
for (var p of params.entries()) {}
十一、ArrayBuffer 对象,Blob 对象
1、ArrayBuffer 对象
ArrayBuffer 对象表示一段二进制数据,用来模拟内存里面的数据。通过这个对象,JavaScript 可以读写二进制数据。这个对象可以看作内存数据的表达。
这个对象是 ES6 才写入标准的,普通的网页编程用不到它,为了教程体系的完整,下面只提供一个简略的介绍,详细介绍请看《ES6 标准入门》里面的章节。
浏览器原生提供ArrayBuffer()
构造函数,用来生成实例。它接受一个整数作为参数,表示这段二进制数据占用多少个字节。
var buffer = new ArrayBuffer(8);
上面代码中,实例对象buffer
占用8个字节。
ArrayBuffer 对象有实例属性byteLength
,表示当前实例占用的内存长度(单位字节)。
var buffer = new ArrayBuffer(8);
buffer.byteLength // 8
ArrayBuffer 对象有实例方法slice()
,用来复制一部分内存。它接受两个整数参数,分别表示复制的开始位置(从0开始)和结束位置(复制时不包括结束位置),如果省略第二个参数,则表示一直复制到结束。
var buf1 = new ArrayBuffer(8);
var buf2 = buf1.slice(0);
上面代码表示复制原来的实例。
2、Blob 对象
2.1 简介
Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写。它通常用来读写文件,它的名字是 Binary Large Object (二进制大型对象)的缩写。它与 ArrayBuffer 的区别在于,它用于操作二进制文件,而 ArrayBuffer 用于操作内存。
浏览器原生提供Blob()
构造函数,用来生成实例对象。
new Blob(array [, options])
Blob
构造函数接受两个参数。第一个参数是数组,成员是字符串或二进制对象,表示新生成的Blob
实例对象的内容;第二个参数是可选的,是一个配置对象,目前只有一个属性type
,它的值是一个字符串,表示数据的 MIME 类型,默认是空字符串。
var htmlFragment = ['<a id="a"><b id="b">hey!</b></a>'];
var myBlob = new Blob(htmlFragment, {type : 'text/html'});
上面代码中,实例对象myBlob
包含的是字符串。生成实例的时候,数据类型指定为text/html
。
下面是另一个例子,Blob 保存 JSON 数据。
var obj = { hello: 'world' };
var blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'});
2.2 实例属性和实例方法
Blob
具有两个实例属性size
和type
,分别返回数据的大小和类型。
var htmlFragment = ['<a id="a"><b id="b">hey!</b></a>'];
var myBlob = new Blob(htmlFragment, {type : 'text/html'});
myBlob.size // 32
myBlob.type // "text/html"
Blob
具有一个实例方法slice
,用来拷贝原来的数据,返回的也是一个Blob
实例。
myBlob.slice(start, end, contentType)
slice
方法有三个参数,都是可选的。它们依次是起始的字节位置(默认为0)、结束的字节位置(默认为size
属性的值,该位置本身将不包含在拷贝的数据之中)、新实例的数据类型(默认为空字符串)。
2.3 获取文件信息
文件选择器<input type="file">
用来让用户选取文件。出于安全考虑,浏览器不允许脚本自行设置这个控件的value
属性,即文件必须是用户手动选取的,不能是脚本指定的。一旦用户选好了文件,脚本就可以读取这个文件。
文件选择器返回一个 FileList 对象,该对象是一个类似数组的成员,每个成员都是一个 File 实例对象。File 实例对象是一个特殊的 Blob 实例,增加了name
和lastModifiedDate
属性。
// HTML 代码如下
// <input type="file" accept="image/*" multiple onchange="fileinfo(this.files)"/>
function fileinfo(files) {
for (var i = 0; i < files.length; i++) {
var f = files[i];
console.log(
f.name, // 文件名,不含路径
f.size, // 文件大小,Blob 实例属性
f.type, // 文件类型,Blob 实例属性
f.lastModifiedDate // 文件的最后修改时间
);
}
}
除了文件选择器,拖放 API 的dataTransfer.files
返回的也是一个FileList 对象,它的成员因此也是 File 实例对象。
2.4 下载文件
AJAX 请求时,如果指定responseType
属性为blob
,下载下来的就是一个 Blob 对象。
function getBlob(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = function () {
callback(xhr.response);
}
xhr.send(null);
}
上面代码中,xhr.response
拿到的就是一个 Blob 对象。
2.5 生成 URL
浏览器允许使用URL.createObjectURL()
方法,针对 Blob 对象生成一个临时 URL,以便于某些 API 使用。这个 URL 以blob://
开头,表明对应一个 Blob 对象,协议头后面是一个识别符,用来唯一对应内存里面的 Blob 对象。这一点与data://URL
(URL 包含实际数据)和file://URL
(本地文件系统里面的文件)都不一样。
var droptarget = document.getElementById('droptarget');
droptarget.ondrop = function (e) {
var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
var type = files[i].type;
if (type.substring(0,6) !== 'image/')
continue;
var img = document.createElement('img');
img.src = URL.createObjectURL(files[i]);
img.onload = function () {
this.width = 100;
document.body.appendChild(this);
URL.revokeObjectURL(this.src);
}
}
}
上面代码通过为拖放的图片文件生成一个 URL,产生它们的缩略图,从而使得用户可以预览选择的文件。
浏览器处理 Blob URL 就跟普通的 URL 一样,如果 Blob 对象不存在,返回404状态码;如果跨域请求,返回403状态码。Blob URL 只对 GET 请求有效,如果请求成功,返回200状态码。由于 Blob URL 就是普通 URL,因此可以下载。
2.6 读取文件
取得 Blob 对象以后,可以通过FileReader
对象,读取 Blob 对象的内容,即文件内容。
FileReader 对象提供四个方法,处理 Blob 对象。Blob 对象作为参数传入这些方法,然后以指定的格式返回。
FileReader.readAsText()
:返回文本,需要指定文本编码,默认为 UTF-8。FileReader.readAsArrayBuffer()
:返回 ArrayBuffer 对象。FileReader.readAsDataURL()
:返回 Data URL。FileReader.readAsBinaryString()
:返回原始的二进制字符串。
下面是FileReader.readAsText()
方法的例子,用来读取文本文件。
// HTML 代码如下
// <input type=’file' onchange='readfile(this.files[0])'></input>
// <pre id='output'></pre>
function readfile(f) {
var reader = new FileReader();
reader.readAsText(f);
reader.onload = function () {
var text = reader.result;
var out = document.getElementById('output');
out.innerHTML = '';
out.appendChild(document.createTextNode(text));
}
reader.onerror = function(e) {
console.log('Error', e);
};
}
上面代码中,通过指定 FileReader 实例对象的onload
监听函数,在实例的result
属性上拿到文件内容。
下面是FileReader.readAsArrayBuffer()
方法的例子,用于读取二进制文件。
// HTML 代码如下
// <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
// 文件开头的四个字节,生成一个 Blob 对象
var slice = file.slice(0, 4);
var reader = new FileReader();
// 读取这四个字节
reader.readAsArrayBuffer(slice);
reader.onload = function (e) {
var buffer = reader.result;
// 将这四个字节的内容,视作一个32位整数
var view = new DataView(buffer);
var magic = view.getUint32(0, false);
// 根据文件的前四个字节,判断它的类型
switch(magic) {
case 0x89504E47: file.verified_type = 'image/png'; break;
case 0x47494638: file.verified_type = 'image/gif'; break;
case 0x25504446: file.verified_type = 'application/pdf'; break;
case 0x504b0304: file.verified_type = 'application/zip'; break;
}
console.log(file.name, file.verified_type);
};
}
十二、File 对象,FileList 对象,FileReader 对象
1、File 对象
File 对象代表一个文件,用来读写文件信息。它继承了 Blob 对象,或者说是一种特殊的 Blob 对象,所有可以使用 Blob 对象的场合都可以使用它。
最常见的使用场合是表单的文件上传控件(``),用户选中文件以后,浏览器就会生成一个数组,里面是每一个用户选中的文件,它们都是 File 实例对象。
// HTML 代码如下
// <input id="fileItem" type="file">
var file = document.getElementById('fileItem').files[0];
file instanceof File // true
上面代码中,file
是用户选中的第一个文件,它是 File 的实例。
1.1 构造函数
浏览器原生提供一个File()
构造函数,用来生成 File 实例对象。
new File(array, name [, options])
File()
构造函数接受三个参数。
- array:一个数组,成员可以是二进制对象或字符串,表示文件的内容。
- name:字符串,表示文件名或文件路径。
- options:配置对象,设置实例的属性。该参数可选。
第三个参数配置对象,可以设置两个属性。
- type:字符串,表示实例对象的 MIME 类型,默认值为空字符串。
- lastModified:时间戳,表示上次修改的时间,默认为
Date.now()
。
下面是一个例子。
var file = new File(
['foo'],
'foo.txt',
{
type: 'text/plain',
}
);
1.2 实例属性和实例方法
File 对象有以下实例属性。
- File.lastModified:最后修改时间
- File.name:文件名或文件路径
- File.size:文件大小(单位字节)
- File.type:文件的 MIME 类型
var myFile = new File([], 'file.bin', {
lastModified: new Date(2018, 1, 1),
});
myFile.lastModified // 1517414400000
myFile.name // "file.bin"
myFile.size // 0
myFile.type // ""
上面代码中,由于myFile
的内容为空,也没有设置 MIME 类型,所以size
属性等于0,type
属性等于空字符串。
File 对象没有自己的实例方法,由于继承了 Blob 对象,因此可以使用 Blob 的实例方法slice()
。
2、FileList 对象
FileList
对象是一个类似数组的对象,代表一组选中的文件,每个成员都是一个 File 实例。它主要出现在两个场合。
- 文件控件节点(``)的
files
属性,返回一个 FileList 实例。 - 拖拉一组文件时,目标区的
DataTransfer.files
属性,返回一个 FileList 实例。
// HTML 代码如下
// <input id="fileItem" type="file">
var files = document.getElementById('fileItem').files;
files instanceof FileList // true
上面代码中,文件控件的files
属性是一个 FileList 实例。
FileList 的实例属性主要是length
,表示包含多少个文件。
FileList 的实例方法主要是item()
,用来返回指定位置的实例。它接受一个整数作为参数,表示位置的序号(从零开始)。但是,由于 FileList 的实例是一个类似数组的对象,可以直接用方括号运算符,即myFileList[0]
等同于myFileList.item(0)
,所以一般用不到item()
方法。
3、FileReader 对象
FileReader 对象用于读取 File 对象或 Blob 对象所包含的文件内容。
浏览器原生提供一个FileReader
构造函数,用来生成 FileReader 实例。
var reader = new FileReader();
FileReader 有以下的实例属性。
- FileReader.error:读取文件时产生的错误对象
- FileReader.readyState:整数,表示读取文件时的当前状态。一共有三种可能的状态,
0
表示尚未加载任何数据,1
表示数据正在加载,2
表示加载完成。 - FileReader.result:读取完成后的文件内容,有可能是字符串,也可能是一个 ArrayBuffer 实例。
- FileReader.onabort:
abort
事件(用户终止读取操作)的监听函数。 - FileReader.onerror:
error
事件(读取错误)的监听函数。 - FileReader.onload:
load
事件(读取操作完成)的监听函数,通常在这个函数里面使用result
属性,拿到文件内容。 - FileReader.onloadstart:
loadstart
事件(读取操作开始)的监听函数。 - FileReader.onloadend:
loadend
事件(读取操作结束)的监听函数。 - FileReader.onprogress:
progress
事件(读取操作进行中)的监听函数。
下面是监听load
事件的一个例子。
// HTML 代码如下
// <input type="file" onchange="onChange(event)">
function onChange(event) {
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function (event) {
console.log(event.target.result)
};
reader.readAsText(file);
}
上面代码中,每当文件控件发生变化,就尝试读取第一个文件。如果读取成功(load
事件发生),就打印出文件内容。
FileReader 有以下实例方法。
- FileReader.abort():终止读取操作,
readyState
属性将变成2
。 - FileReader.readAsArrayBuffer():以 ArrayBuffer 的格式读取文件,读取完成后
result
属性将返回一个 ArrayBuffer 实例。 - FileReader.readAsBinaryString():读取完成后,
result
属性将返回原始的二进制字符串。 - FileReader.readAsDataURL():读取完成后,
result
属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于<img>
元素的src
属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀data:*/*;base64,
从字符串里删除以后,再进行解码。 - FileReader.readAsText():读取完成后,
result
属性将返回文件内容的文本字符串。该方法的第一个参数是代表文件的 Blob 实例,第二个参数是可选的,表示文本编码,默认为 UTF-8。
下面是一个例子。
/* HTML 代码如下
<input type="file" onchange="previewFile()">
<img src="" height="200">
*/
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener('load', function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
上面代码中,用户选中图片文件以后,脚本会自动读取文件内容,然后作为一个 Data URL 赋值给<img>
元素的src
属性,从而把图片展示出来。
十三、表单,FormData 对象
1、表单概述
表单(<form>
)用来收集用户提交的数据,发送到服务器。比如,用户提交用户名和密码,让服务器验证,就要通过表单。表单提供多种控件,让开发者使用,具体的控件种类和用法请参考 HTML 语言的教程。本章主要介绍 JavaScript 与表单的交互。
<form action="/handling-page" method="post">
<div>
<label for="name">用户名:</label>
<input type="text" id="name" name="user_name" />
</div>
<div>
<label for="passwd">密码:</label>
<input type="password" id="passwd" name="user_passwd" />
</div>
<div>
<input type="submit" id="submit" name="submit_button" value="提交" />
</div>
</form>
上面代码就是一个简单的表单,包含三个控件:用户名输入框、密码输入框和提交按钮。
用户点击“提交”按钮,每一个控件都会生成一个键值对,键名是控件的name
属性,键值是控件的value
属性,键名和键值之间由等号连接。比如,用户名输入框的name
属性是user_name
,value
属性是用户输入的值,假定是“张三”,提交到服务器的时候,就会生成一个键值对user_name=张三
。
所有的键值对都会提交到服务器。但是,提交的数据格式跟<form>
元素的method
属性有关。该属性指定了提交数据的 HTTP 方法。如果是 GET 方法,所有键值对会以 URL 的查询字符串形式,提交到服务器,比如/handling-page?user_name=张三&user_passwd=123&submit_button=提交
。下面就是 GET 请求的 HTTP 头信息。
GET /handling-page?user_name=张三&user_passwd=123&submit_button=提交
Host: example.com
如果是 POST 方法,所有键值对会连接成一行,作为 HTTP 请求的数据体发送到服务器,比如user_name=张三&user_passwd=123&submit_button=提交
。下面就是 POST 请求的头信息。
POST /handling-page HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 74
user_name=张三&user_passwd=123&submit_button=提交
注意,实际提交的时候,只要键值不是 URL 的合法字符(比如汉字“张三”和“提交”),浏览器会自动对其进行编码。
点击submit
控件,就可以提交表单。
<form>
<input type="submit" value="提交">
</form>
上面表单就包含一个submit
控件,点击这个控件,浏览器就会把表单数据向服务器提交。
注意,表单里面的``元素如果没有用type
属性指定类型,那么默认就是submit
控件。
<form>
<button>提交</button>
</form>
上面表单的``元素,点击以后也会提交表单。
除了点击submit
控件提交表单,还可以用表单元素的submit()
方法,通过脚本提交表单。
formElement.submit();
表单元素的reset()
方法可以重置所有控件的值(重置为默认值)。
formElement.reset()
2、FormData 对象
2.1 概述
表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成过程,构造和编辑表单键值对,然后通过XMLHttpRequest.send()
方法发送。浏览器原生提供了 FormData 对象来完成这项工作。
FormData 首先是一个构造函数,用来生成实例。
var formdata = new FormData(form);
FormData()
构造函数的参数是一个表单元素,这个参数是可选的。如果省略参数,就表示一个空的表单,否则就会处理表单元素里面的键值对。
下面是一个表单。
<form id="myForm" name="myForm">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
</div>
<div>
<label for="useracc">账号:</label>
<input type="text" id="useracc" name="useracc">
</div>
<div>
<label for="userfile">上传文件:</label>
<input type="file" id="userfile" name="userfile">
</div>
<input type="submit" value="Submit!">
</form>
我们用 FormData 对象处理上面这个表单。
var myForm = document.getElementById('myForm');
var formData = new FormData(myForm);
// 获取某个控件的值
formData.get('username') // ""
// 设置某个控件的值
formData.set('username', '张三');
formData.get('username') // "张三"
2.2 实例方法
FormData 提供以下实例方法。
FormData.get(key)
:获取指定键名对应的键值,参数为键名。如果有多个同名的键值对,则返回第一个键值对的键值。FormData.getAll(key)
:返回一个数组,表示指定键名对应的所有键值。如果有多个同名的键值对,数组会包含所有的键值。FormData.set(key, value)
:设置指定键名的键值,参数为键名。如果键名不存在,会添加这个键值对,否则会更新指定键名的键值。如果第二个参数是文件,还可以使用第三个参数,表示文件名。FormData.delete(key)
:删除一个键值对,参数为键名。FormData.append(key, value)
:添加一个键值对。如果键名重复,则会生成两个相同键名的键值对。如果第二个参数是文件,还可以使用第三个参数,表示文件名。FormData.has(key)
:返回一个布尔值,表示是否具有该键名的键值对。FormData.keys()
:返回一个遍历器对象,用于for...of
循环遍历所有的键名。FormData.values()
:返回一个遍历器对象,用于for...of
循环遍历所有的键值。FormData.entries()
:返回一个遍历器对象,用于for...of
循环遍历所有的键值对。如果直接用for...of
循环遍历 FormData 实例,默认就会调用这个方法。
下面是get()
、getAll()
、set()
、append()
方法的例子。
var formData = new FormData();
formData.set('username', '张三');
formData.append('username', '李四');
formData.get('username') // "张三"
formData.getAll('username') // ["张三", "李四"]
formData.append('userpic[]', myFileInput.files[0], 'user1.jpg');
formData.append('userpic[]', myFileInput.files[1], 'user2.jpg');
下面是遍历器的例子。
var formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
for (var key of formData.keys()) {
console.log(key);
}
// "key1"
// "key2"
for (var value of formData.values()) {
console.log(value);
}
// "value1"
// "value2"
for (var pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
// key1: value1
// key2: value2
// 等同于遍历 formData.entries()
for (var pair of formData) {
console.log(pair[0] + ': ' + pair[1]);
}
// key1: value1
// key2: value2
3、表单的内置验证
3.1 自动校验
表单提交的时候,浏览器允许开发者指定一些条件,它会自动验证各个表单控件的值是否符合条件。
<!-- 必填 -->
<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;
}
3.2 checkValidity()
除了提交表单的时候,浏览器自动校验表单,还可以手动触发表单的校验。表单元素和表单控件都有checkValidity()
方法,用于手动触发校验。
// 触发整个表单的校验
form.checkValidity()
// 触发单个表单控件的校验
formControl.checkValidity()
checkValidity()
方法返回一个布尔值,true
表示通过校验,false
表示没有通过校验。因此,提交表单可以封装为下面的函数。
function submitForm(action) {
var form = document.getElementById('form');
form.action = action;
if (form.checkValidity()) {
form.submit();
}
}
3.3 willValidate 属性
控件元素的willValidate
属性是一个布尔值,表示该控件是否会在提交时进行校验。
// HTML 代码如下
// <form novalidate>
// <input id="name" name="name" required />
// </form>
var input = document.querySelector('#name');
input.willValidate // true
3.4 validationMessage 属性
控件元素的validationMessage
属性返回一个字符串,表示控件不满足校验条件时,浏览器显示的提示文本。以下两种情况,该属性返回空字符串。
- 该控件不会在提交时自动校验
- 该控件满足校验条件
// HTML 代码如下
// <form><input type="text" required></form>
document.querySelector('form input').validationMessage
// "请填写此字段。"
下面是另一个例子。
var myInput = document.getElementById('myinput');
if (!myInput.checkValidity()) {
document.getElementById('prompt').innerHTML = myInput.validationMessage;
}
3.5 setCustomValidity()
控件元素的setCustomValidity()
方法用来定制校验失败时的报错信息。它接受一个字符串作为参数,该字符串就是定制的报错信息。如果参数为空字符串,则上次设置的报错信息被清除。
这个方法可以替换浏览器内置的表单验证报错信息,参数就是要显示的报错信息。
<form action="somefile.php">
<input
type="text"
name="username"
placeholder="Username"
pattern="[a-z]{1,15}"
id="username"
>
<input type="submit">
</form>
上面的表单输入框,要求只能输入小写字母,且不得超过15个字符。如果输入不符合要求(比如输入“ABC”),提交表单的时候,Chrome 浏览器会弹出报错信息“Please match the requested format.”,禁止表单提交。下面使用setCustomValidity()
方法替换掉报错信息。
var input = document.getElementById('username');
input.oninvalid = function (event) {
event.target.setCustomValidity(
'用户名必须是小写字母,不能为空,最长不超过15个字符'
);
}
上面代码中,setCustomValidity()
方法是在invalid
事件的监听函数里面调用。该方法也可以直接调用,这时如果参数不为空字符串,浏览器就会认为该控件没有通过校验,就会立刻显示该方法设置的报错信息。
/* HTML 代码如下
<form>
<p><input type="file" id="fs"></p>
<p><input type="submit"></p>
</form>
*/
document.getElementById('fs').onchange = checkFileSize;
function checkFileSize() {
var fs = document.getElementById('fs');
var files = fs.files;
if (files.length > 0) {
if (files[0].size > 75 * 1024) {
fs.setCustomValidity('文件不能大于 75KB');
return;
}
}
fs.setCustomValidity('');
}
上面代码一旦发现文件大于 75KB,就会设置校验失败,同时给出自定义的报错信息。然后,点击提交按钮时,就会显示报错信息。这种校验失败是不会自动消除的,所以如果所有文件都符合条件,要将报错信息设为空字符串,手动消除校验失败的状态。
3.6 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
:布尔值,表示用户没有填入必填的值。
下面是一个例子。
var input = document.getElementById('myinput');
if (input.validity.valid) {
console.log('通过校验');
} else {
console.log('校验失败');
}
下面是另外一个例子。
var txt = '';
if (document.getElementById('myInput').validity.rangeOverflow) {
txt = '数值超过上限';
}
document.getElementById('prompt').innerHTML = txt;
如果想禁止浏览器弹出表单验证的报错信息,可以监听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';
}
});
上面代码中,一旦发生invalid
事件(表单验证失败),event.preventDefault()
用来禁止浏览器弹出默认的验证失败提示,然后设置定制的报错提示框。
3.7 表单的 novalidate 属性
表单元素的 HTML 属性novalidate
,可以关闭浏览器的自动校验。
<form novalidate>
</form>
这个属性也可以在脚本里设置。
form.noValidate = true;
如果表单元素没有设置novalidate
属性,那么提交按钮(或
元素)的formnovalidate
属性也有同样的作用。
<form>
<input type="submit" value="submit" formnovalidate>
</form>
4、enctype 属性
表单能够用四种编码,向服务器发送数据。编码格式由表单的enctype
属性决定。
假定表单有两个字段,分别是foo
和baz
,其中foo
字段的值等于bar
,baz
字段的值是一个分为两行的字符串。
The first line.
The second line.
下面四种格式,都可以将这个表单发送到服务器。
(1)GET 方法
如果表单使用GET
方法发送数据,enctype
属性无效。
<form
action="register.php"
method="get"
onsubmit="AJAXSubmit(this); return false;"
>
</form>
数据将以 URL 的查询字符串发出。
?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.
(2)application/x-www-form-urlencoded
如果表单用POST
方法发送数据,并省略enctype
属性,那么数据以application/x-www-form-urlencoded
格式发送(因为这是默认值)。
<form
action="register.php"
method="post"
onsubmit="AJAXSubmit(this); return false;"
>
</form>
发送的 HTTP 请求如下。
Content-Type: application/x-www-form-urlencoded
foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
上面代码中,数据体里面的%0D%0A
代表换行符(\r\n
)。
(3)text/plain
如果表单使用POST
方法发送数据,enctype
属性为text/plain
,那么数据将以纯文本格式发送。
<form
action="register.php"
method="post"
enctype="text/plain"
onsubmit="AJAXSubmit(this); return false;"
>
</form>
发送的 HTTP 请求如下。
Content-Type: text/plain
foo=bar
baz=The first line.
The second line.
(4)multipart/form-data
如果表单使用POST
方法,enctype
属性为multipart/form-data
,那么数据将以混合的格式发送。
<form
action="register.php"
method="post"
enctype="multipart/form-data"
onsubmit="AJAXSubmit(this); return false;"
>
</form>
发送的 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--
这种格式也是文件上传的格式。
5、文件上传
用户上传文件,也是通过表单。具体来说,就是通过文件输入框选择本地文件,提交表单的时候,浏览器就会把这个文件发送到服务器。
<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
属性,指定可以一次选择多个文件;如果没有这个属性,则一次只能选择一个文件。
var fileSelect = document.getElementById('file');
var files = fileSelect.files;
然后,新建一个 FormData 实例对象,模拟发送到服务器的表单数据,把选中的文件添加到这个对象上面。
var formData = new FormData();
for (var i = 0; i < files.length; i++) {
var file = files[i];
// 只上传图片文件
if (!file.type.match('image.*')) {
continue;
}
formData.append('photos[]', file, file.name);
}
最后,使用 Ajax 向服务器上传文件。
var xhr = new XMLHttpRequest();
xhr.open('POST', 'handler.php', true);
xhr.onload = function () {
if (xhr.status !== 200) {
console.log('An error occurred!');
}
};
xhr.send(formData);
除了发送 FormData 实例,也可以直接 AJAX 发送文件。
var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();
xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
- 点赞
- 收藏
- 关注作者
评论(0)