# 网络请求-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) —— 修改一个字段为文件

此外还有 deletegethas方法

# 发送带有文件的表单

表单始终以 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),也可以创建一个完全没有表单的对象,然后使用表单方法来附加字段