通过 JavaScript 发送表单
当用户提交 HTML 表单时,例如通过点击 提交按钮,浏览器会向指定的 HTTP 请求发送表单中的数据。但与这种声明式的方法不同,Web 应用有时会使用 JavaScript API,如 fetch(),以编程方式将数据发送到期望表单提交的端点。本文将解释为什么这是一个重要的用例以及如何实现它。
为什么使用 JavaScript 提交表单数据?
正如我们在关于 发送表单数据 的文章中所述,标准的 HTML 表单提交会加载数据发送到的 URL,这意味着浏览器窗口会进行整页刷新导航。
然而,许多 Web 应用,特别是 渐进式 Web 应用 和 单页应用,使用 JavaScript API 从服务器请求数据并更新页面上相关部分,从而避免了整页刷新的开销。
因此,当这些 Web 应用想要提交表单数据时,它们仅使用 HTML 表单来收集用户输入,而不是用于数据提交。当用户尝试发送数据时,应用程序会接管控制权,并使用 JavaScript API(如 fetch())发送数据。
JavaScript 表单提交的问题
如果 Web 应用发送表单数据的服务器端点在 Web 应用开发者的控制之下,那么他们可以按任意方式发送表单数据:例如,作为 JSON 对象。
但是,如果服务器端点期望表单提交,Web 应用必须以特定方式编码数据。例如,如果数据只是文本,它将由 URL 编码的键/值对列表组成,并以 application/x-www-form-urlencoded 的 Content-Type 发送。如果表单包含二进制数据,则必须使用 multipart/form-data 内容类型发送。
FormData 接口负责以这种方式编码数据的过程,在本文的其余部分,我们将快速介绍 FormData。有关更多详细信息,请参阅我们关于 使用 FormData 对象 的指南。
手动构建 FormData 对象
您可以通过为要添加的每个字段调用对象的 append() 方法来填充 FormData 对象,传入字段的名称和值。值可以是字符串(用于文本字段)或 Blob(用于二进制字段,包括 File 对象)。
在下面的示例中,当用户点击按钮时,我们将数据作为表单提交发送
async function sendData(data) {
// Construct a FormData instance
const formData = new FormData();
// Add a text field
formData.append("name", "Pomegranate");
// Add a file
const selection = await window.showOpenFilePicker();
if (selection.length > 0) {
const file = await selection[0].getFile();
formData.append("file", file);
}
try {
const response = await fetch("https://example.org/post", {
method: "POST",
// Set the FormData instance as the request body
body: formData,
});
console.log(await response.json());
} catch (e) {
console.error(e);
}
}
const send = document.querySelector("#send");
send.addEventListener("click", sendData);
-
我们首先构造一个新、空的
FormData对象。 -
接下来,我们调用两次
append(),将两个项目添加到FormData对象中:一个文本字段和一个文件。 -
最后,我们使用
fetch()API 发起一个POST请求,将FormData对象设置为请求体。
请注意,我们无需设置 Content-Type 标头:当我们向 fetch() 传递 FormData 对象时,会自动设置正确的标头。
关联 FormData 对象和 <form>
如果您的数据实际上来自一个 <form>,您可以通过将表单传递给 FormData 构造函数来填充 FormData 实例。
假设我们的 HTML 声明了一个 <form> 元素
<form id="userinfo">
<p>
<label for="username">Enter your name:</label>
<input type="text" id="username" name="username" value="Dominic" />
</p>
<p>
<label for="avatar">Select an avatar</label>
<input type="file" id="avatar" name="avatar" required />
</p>
<input type="submit" value="Submit" />
</form>
该表单包含一个文本输入框、一个文件输入框和一个提交按钮。
JavaScript 代码如下
const form = document.querySelector("#userinfo");
async function sendData() {
// Associate the FormData object with the form element
const formData = new FormData(form);
try {
const response = await fetch("https://example.org/post", {
method: "POST",
// Set the FormData instance as the request body
body: formData,
});
console.log(await response.json());
} catch (e) {
console.error(e);
}
}
// Take over form submission
form.addEventListener("submit", (event) => {
event.preventDefault();
sendData();
});
我们为表单元素添加了一个提交事件处理程序。它首先调用 preventDefault() 来阻止浏览器内置的表单提交,以便我们接管。然后我们调用 sendData(),它会检索表单元素并将其传递给 FormData 构造函数。
之后,我们使用 fetch() 将 FormData 实例作为 HTTP POST 请求发送。