ToolkitX
知识库工具箱

表单与验证

input 类型、表单校验、提交处理

20min·入门

01. 表单的基本结构

表单是用户往网站输入数据的最主要方式。form 标签包裹所有表单项,action 属性写提交地址,method 属性写请求方式 GET 或 POST。每个输入项用 label 加 input 组合,label 的 for 属性关联 input 的 id,点了标签就能聚焦输入框,对移动端点击小按钮尤其友好。
html
<form action="/submit" method="POST">
  <label for="email">邮箱</label>
  <input type="email" id="email" name="email" required />
  <button type="submit">提交</button>
</form>

02. 各种 input 类型

HTML5 提供了超级丰富的 input 类型,不只是 text。email 会自动校验邮箱格式,number 弹出数字键盘,date 弹出日期选择器,tel 弹出拨号键盘。移动端用对了 type 用户体验天差地别。color 直接打开取色器,range 展示滑块。这些原生控件比手写的好用得多。
html
<input type="email" placeholder="[email protected]" />
<input type="number" min="0" max="100" step="5" />
<input type="date" />
<input type="tel" />
<input type="color" />
<input type="range" min="0" max="100" />
<input type="file" accept="image/*" multiple />
能用原生 HTML5 input 类型就尽量用,别动不动就自己写选择器组件。

03. 表单验证

表单验证有两层:前端验证和后端验证。前端验证是为了立刻提示用户填错了什么,后端验证才是安全的保障。HTML5 自带验证属性:required 表示必填,pattern 写正则,min/max 限范围。自定义错误提示用 setCustomValidity。
html
<input required minlength="6" maxlength="20" />
<input type="url" />
<input pattern="[0-9]{11}" title="请输入11位手机号" />

// JS 自定义验证
input.setCustomValidity("用户名已存在")
input.reportValidity()

// 阻止默认气泡改用自定义样式
form.addEventListener("submit", (e) => {
  if (!form.checkValidity()) e.preventDefault()
})

04. 表单的可用性优化

好的表单不光要能用还要好用。自动聚焦第一个输入框,用 tabindex 控制 Tab 键切换顺序,分组用 fieldset 加 legend。错误提示要紧挨着出错的输入框,不要一股脑堆在顶部。提交按钮要防重复点击,点了之后变灰加 loading 状态。
html
<form novalidate>
  <fieldset>
    <legend>个人信息</legend>
    <input autofocus autocomplete="given-name" />
    <input autocomplete="email" />
  </fieldset>
  
  <fieldset>
    <legend>收货地址</legend>
    <input autocomplete="street-address" />
  </fieldset>
  
  <button type="submit" id="submitBtn">提交</button>
</form>
autocomplete 属性配合规范的值能帮用户快速自动填充,对移动端极其友好。

05. 表单数据和文件上传

普通表单提交会刷新页面,前端通常拦截后用 AJAX 发送 FormData。FormData 自动收集所有带 name 属性的表单字段,包括文件。如果不用 FormData,也可以用 new URLSearchParams 序列化数据。文件上传要用 multipart/form-data 编码。
javascript
const form = document.querySelector('form')
form.addEventListener('submit', async (e) => {
  e.preventDefault()
  const formData = new FormData(form)
  const res = await fetch('/api/submit', {
    method: 'POST',
    body: formData,
  })
})
FormData 不会收集没有 name 属性的字段,这是常见的坑。

知识测验

1/4正确 0

label 标签的 for 属性关联 input 的什么属性?