# 网络请求-From
JavaScript 较早的一个用途是承担一部分服务器端表单处理的责任。 虽然 Web 和 JavaScript 都已经发展了很多年,但 Web 表单的变化不是很大。由于不能直接使用表单解决问题,因此开发者不得不使用 JavaScript 既做表单验证,又用于增强标准表单控件的默认行为。
# 表单属性和方法
表单(form)以及例如 <input>
的控件(control)元素有许多特殊的属性和事件。
# 表单和元素获取
文档中的表单是特殊集合 document.forms 的成员。
let firstForm = document.forms[0] - 文档中的第一个表单;
当我们有了一个表单时,其中的任何元素都可以通过命名的集合 form.elements 来获取到。
<form name="my">
<input name="one" value="1" />
<input name="two" value="2" />
</form>
// 获取表单
let form = document.forms.my; // <form name="my"> 元素
// 获取表单中的元素
let elem = form.elements.one; // <input name="one"> 元素
console.log(elem.value); // 1
# 表单控件元素
- input 和 textarea
我们可以通过 input.value(字符串)或 input.checked(布尔值)来访问复选框(checkbox)中的它们的 value。
input.value = "New value";
textarea.value = "New text"; // 注意 textarea也要使用value
- select 和 option
一个 <select>
元素有 3 个重要的属性:
select.options —— <option>
的子元素的集合,
select.value —— 当前所选择的 <option>
的 value,
select.selectedIndex —— 当前所选择的 <option>
的编号。
它们提供了三种为 <select>
设置 value 的不同方式:
找到对应的 <option>
元素,并将 option.selected 设置为 true。
将 select.value 设置为对应的 value。
将 select.selectedIndex 设置为对应 <option>
的编号。
# 表单控件聚焦
# focus/blur 事件
当元素聚焦时,会触发 focus 事件,当元素失去焦点时,会触发 blur 事件。
# focus/blur 方法
elem.focus() 和 elem.blur() 方法可以设置和移除元素上的焦点。
# tabindex
默认情况下,很多元素不支持聚焦。
列表(list)在不同的浏览器表现不同,但有一件事总是正确的:focus/blur 保证支持那些用户可以交互的元素:<button>
,<input>
,<select>
,<a>
等。
使用 HTML-特性(attribute)tabindex 可以改变这种情况。
任何具有 tabindex 特性的元素,都会变成可聚焦的。该特性的 value 是当使用 Tab(或类似的东西)在元素之间进行切换时,元素的顺序号。
属性 elem.tabIndex 也有效
我们可以使用 elem.tabIndex 通过 JavaScript 来添加 tabindex。效果是一样的。
# focus/blur 委托
focus 和 blur 事件不会向上冒泡。
例如,我们不能把 onfocus 放在 <form>
上来对其进行高亮,像这样:
<!-- on focusing in the form -- add the class -->
<form onfocus="this.className='focused'">
<input type="text" name="name" value="Name" />
<input type="text" name="surname" value="Surname" />
</form>
<style>
.focused {
outline: 1px solid red;
}
</style>
focus/blur 不会向上冒泡,但会在捕获阶段向下传播
<form id="form">
<input type="text" name="name" value="Name" />
<input type="text" name="surname" value="Surname" />
</form>
<style>
.focused {
outline: 1px solid red;
}
</style>
<script>
// 将处理程序置于捕获阶段(最后一个参数为 true)
form.addEventListener("focus", () => form.classList.add("focused"), true);
form.addEventListener("blur", () => form.classList.remove("focused"), true);
</script>
# 表单控件事件
# change
当元素更改完成时,将触发 change 事件。
对于文本输入框,当其失去焦点时,就会触发 change 事件。
# input
每当用户对输入值进行修改后,就会触发 input 事件。
无法阻止 oninput 中的任何事件
当输入值更改后,就会触发 input 事件。
所以,我们无法使用 event.preventDefault() —— 已经太迟了,不会起任何作用了。
# 更多表单事件
表单事件 | |
---|---|
input | 当<input> 、<select> 、<textarea> 的值发生变化时,会连续触发 |
select | 当在<input> 、<textarea> 里面选中文本时 |
change | 当<input> 、<input> 、<textarea> 的值发生变化时,修改完成时才触发 |
invalid | 当表单元素的值不满足检验条件时触发 |
reset | 当表单重置时触发 |
submit | 当表单数据向服务器提交时触发 |
# 提交表单
提交表单时,会触发 submit 事件,它通常用于在将表单发送到服务器之前对表单进行校验,或者中止提交,并使用 JavaScript 来处理表单。
提交表单主要有两种方式:
- 第一种 —— 点击
<input type="submit">
或<input type="image">
。 - 第二种 —— 在表单只有单个
input
输入框中按下 Enter 键。
这两个行为都会触发表单的 submit 事件。处理程序可以检查数据,如果有错误,就显示出来,并调用 event.preventDefault(),这样表单就不会被发送到服务器了。
如果要手动将表单提交到服务器,我们可以调用 form.submit()。
有时该方法被用来手动创建和发送表单,如下所示:
let form = document.createElement("form");
form.action = "https://google.com/search";
form.method = "GET";
form.innerHTML = '<input name="q" value="test">';
// 该表单必须在文档中才能提交
document.body.append(form);
form.submit();
# FormData
我们可以通过表单元素发起表单请求,但是只通过 JavaScript 呢? FormData 对象可以提供帮助,它是表示 HTML 表单数据的对象。
构造函数时:
let formData = new FormData([form]);
如果提供了 HTML form
元素,它会自动捕获 form
元素内的表单字段。
fetch
可以接受一个 FormData 对象作为 body
。它会被编码并发送出去,带有 Content-Type: multipart/form-data
。
从服务器角度来看,它就像是一个普通的表单提交。
# 发送一个简单的表单
我们先来发送一个简单的表单。
正如你所看到的,它几乎就是一行代码:
<form id="formElem">
<input type="text" name="name" value="John">
<input type="text" name="surname" value="Smith">
<input type="submit">
</form>
<script>
let response = await fetch('/article/formdata/post/user', {
method: 'POST',
body: new FormData(formElem)
});
</script>
# FormData 方法
我们可以使用以下方法修改 FormData 中的字段:
- formData.append(name, value) —— 添加具有给定 name 和 value 的表单字段
- formData.append(name, blob, fileName) —— 添加一个文件
- formData.set(name, value) —— 修改 name 字段的值为 value
- formData.set(name, blob, fileName) —— 修改一个字段为文件
此外还有 delete
,get
,has
方法
# 发送带有文件的表单
表单始终以 Content-Type: multipart/form-data 来发送数据,这个编码允许发送文件。
因此 <input type="file">
字段也能被发送,类似于普通的表单提交。
<form id="formElem">
<input type="text" name="firstName" value="John" />
Picture:
<input type="file" name="picture" accept="image/*" />
<input type="submit" />
</form>
<script>
let response = await fetch('/article/formdata/post/user-avatar', {
method: 'POST',
body: new FormData(formElem)
});
</script>
# 发送具有 Blob 数据的表单
有时候我们的文件并不一定来自用户手动,而是来自动态生成的,比如 Canvas,这时我们可以将其作为表单的一部分发送,并且,服务器通常更适合接收多部分编码的表单(multipart-encoded form),而不是原始的二进制数据。
<canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>
<input type="button" value="Submit" onclick="submit()" />
<script>
async function submit() {
let imageBlob = await new Promise((resolve) => canvasElem.toBlob(resolve, "image/png"));
let formData = new FormData();
formData.append("firstName", "John");
formData.append("image", imageBlob, "image.png");
let response = await fetch("/article/formdata/post/user-avatar", {
method: "POST",
body: formData
});
let result = await response.json();
console.log(result);
}
</script>
# 总结
FormData 对象用于捕获 HTML 表单,并使用 fetch 或其他网络方法提交。
我们可以从 HTML 表单创建 new FormData(form),也可以创建一个完全没有表单的对象,然后使用表单方法来附加字段