Skip to content

A34. 表单

⭐ 4.1. form 元素

此前我们一直在聚焦 JavaScript 的动态操作,但是我们一直没有获得用户的输入——尤其是数字与文本。而表单就是用来收集用户输入的工具。

⭐ 4.1.1. 表单的基本结构

html
<form>
  <label for="username">用户名:</label>
  <input type="text" id="username" name="username">
  <button type="submit">提交</button>
</form>

<form> 是表单容器,包裹所有表单控件。其中的 labelinput 通过 forid 关联,形成输入控件的逻辑单元。

TIP

表单控件一般需包含在 <form> 内部,才能便于 JavaScript 统一处理。

⭐ 4.1.2. 表单的属性

html
<!-- 默认提交行为演示 -->
<form action="/submit" method="GET">
  <input type="text" name="search" placeholder="搜索关键词">
  <button type="submit">提交</button>
</form>

<!-- 阻止默认行为演示 -->
<form onsubmit="event.preventDefault(); console.log('提交被阻止');">
  <input type="text" name="test">
  <button type="submit">提交</button>
</form>

<form> 元素支持以下属性:

  1. method:
    • GET:参数附加在 URL 后(适合搜索),刷新页面可重现。
    • POST:参数在请求体中发送(适合敏感数据)。
  2. action: 传统后端处理的目标地址,现代前端开发中常被 JavaScript 覆盖。

历史遗留问题

在传统前端开发中,前端只作为界面的辅助,表单的提交行为通常由后端处理,页面自动跳转,浏览器请求服务器渲染新的网页。

而在现代前端开发中,表单的提交行为通常由 JavaScript 处理,然后由 JavaScript 再渲染网页。这时默认提交行为导致的页面跳转便成了累赘,需通过 event.preventDefault() 阻止。

4.2. 🌟 表单核心控件 input

form 只是一个骨架,内部需要有填充数据的控件:input 元素便是表单的核心控件,它可以用于输入各种类型的数据。

4.2.1. ⭐ label 元素的用法

label 元素用于定义表单控件的说明文本,通常与 input 元素一起使用,用于增强用户体验,防止 input 过小不便操作。

1. 使用 for 属性显示绑定

for,即“为了”,用于 label 元素,表示它与相应 ID 的表单控件相关联,点击 label 中的文本也可以激活 input 控件。

html
<p>
  <label for="email">邮箱:</label>
  <input type="email" id="email" name="email">
</p>

2. 嵌套结构隐式绑定

而若觉得设置 idfor 太麻烦,可以直接将 input 元素嵌套在 label 元素内部,此时 label 元素会自动绑定到 input 元素。

html
<label>
  <span>邮箱</span>
  <input type="email" id="email" name="email">
</label>

注意事项

  1. label 元素的 for 属性与 input 元素的 id 必须一致。
  2. 若采用嵌套式结构,label 元素内只应出现唯一的表单控件,其余部分都是普通的提示文本。

4.2.2. 🌟 input 元素的属性

属性名作用
type定义输入类型(如 textpasswordnumber 等),决定输入行为和验证规则
value设置初始值值
name表单提交时字段的标识符
autocomplete控制浏览器自动填充行为(支持 on/off/username/email 等具体字段)
html
<input type="text" name="username" autocomplete="username">
<input type="email" name="email" autocomplete="email">

其中:

  • type 是最核心属性,直接影响输入格式和验证。
  • autocomplete 的具体值(如 username)能引导浏览器智能填充数据。

TIP

  1. 表单元素按最佳实践应填写 name,保证语义化。
  2. 表单元素的第一步是确定何种类型。

接下来我们便介绍几种常见的 input 类型,和它在这些类型下特有的属性。

4.2.3. 🌟 普通文本框与密码框

最常见的需求是输入文本:

html
<label>
  <span>用户名</span>
  <input type="text" name="username" autocomplete="username">
</label>

<label>
  <span>密码</span>
  <input type="password" name="password" autocomplete="current-password">
</label>

文本框会显示用户输入内容,适合用户名等公开信息,而 密码框 则隐藏输入内容(显示为圆点),适合密码等敏感信息。

4.2.4. 🌟 数字输入框

需要数字输入的场景,如年龄、价格等,可使用 number 类型。

html
<label for="age">年龄:</label>
<input type="number" id="age" name="age" min="0" max="150" step="1">

属性说明

属性名作用默认值
min最小值
max最大值
step输入步长1

默认情况下,number 类型允许用户输入任意整数,也可以将 step 设置为 0.010.050.10.5 等小数(或 510 等大整数),实现更精细的输入控制;或者直接使用 step="any" 允许用户输入任意精度的数字。

4.2.5. ⭐ 日期输入框

需要选择日期的场景,如出生日期、活动日期等,可使用 date 类型,这样就不需要用户用纯文本手动输入了。

html
<label for="birthday">出生日期:</label>
<input type="date" id="birthday" name="birthday" min="1900-01-01" max="2025-05-12">

其中 minmax 限制了可选日期范围。

4.2.6. ⭐ 单选框

单选框是一组互斥的选项,用户只能选择其中一个。

与前面的控件不同,它必须设置 name 属性用于标识同一组选项,并且需要设置的 value 不再表示默认值,而是选中该元素后表单数据的值。

同时,由于单选框在界面上很小,使用 label 元素绑定几乎是必须的。

html
<p>性别:</p>
<input type="radio" id="male" name="gender" value="male">
<label for="male">男</label>

<input type="radio" id="female" name="gender" value="female">
<label for="female">女</label>

4.2.7. ⭐ 复选框

复选框是一组可多选的选项,用户可以选择任意个选项。

与单选框类似,它也必须通过 name 属性用于标识同一组选项,并且需要设置的 value 也不是默认值,而是选中该元素后表单数据的值。

html
<p>兴趣爱好:</p>
<input type="checkbox" id="reading" name="hobby" value="reading">
<label for="reading">阅读</label>

<input type="checkbox" id="sports" name="hobby" value="sports">
<label for="sports">运动</label>

4.2.8. ⭐ 文件上传框

有时用户需要的不仅仅是文本,还需要上传文件,这时可以使用 file 类型。

html
<label for="attachments">上传附件:</label>
<input type="file" id="attachments" name="attachments" accept=".pdf,.docx" multiple>

其中:

  • accept 属性用于限制文件类型,若有多个则使用英文逗号分隔,例如 .pdf,.docx 表示只允许上传 PDF 和 Word 文档。
  • multiple 属性用于允许用户选择多个文件。这是 HTML 的一种特殊属性,不需要设置值,根据该属性是否存在来决定是否允许多选。也就是说,如果只允许上传一个文件,不写 multiple 属性即可。

4.2.9. 隐藏域

html
<input type="hidden" name="startTime" value="2025-05-12T12:00:00">

隐藏域用于存储不可见的表单数据(如问卷开始时间),提交时随表单一同发送,常用于前后端交互中传递上下文信息,在现代前端开发中不再常用。

4.3. ⭐ 表单其他控件

除了 input 元素,表单还包含其他控件,如 selecttextareabutton 等。

4.3.1. ⭐ 下拉框

单选和多选不一定要把全部选项罗列在界面上,也可以下拉选中,使界面更加简洁。

html
<label for="city">城市:</label>
<select id="city" name="city">
  <option value="beijing">北京</option>
  <option value="shanghai">上海</option>
  <option value="guangzhou">广州</option>
</select>

其语法为,在 <select> 元素下使<option> 元素定义选项每个选项都必须有一个 value 属性。最后选中哪个选项,则表单数据的值为该选项的 value

4.3.2. ⭐ 多行文本框

html
<label for="bio">个人简介:</label>
<textarea id="bio" name="bio" rows="4" cols="50"></textarea>

注意事项

  • <textarea> 元素的默认值通过闭合标签内的文本设置。
  • <textarea> 的换行和缩进会被保留至输入框内,建议在 HTML 中一行写完默认值。
  • rowscols 定义行与列的字符数而非像素数,比如 rows="4" 表示大约展示 4 行的高度。

4.3.3. 🌟 按钮元素

最后请出一个非常重要的控件:按钮元素,用于提交表单执行其他操作

html
<button type="submit">提交</button>
<button type="reset">重置</button>
<button type="button" onclick="alert('点击了按钮')">自定义操作</button>
type 属性值作用
submit提交表单
reset重置表单(不常用)
button自定义操作

4.4. 🌟 表单 DOM 交互

前面我们已经认识了如何在 HTML 中完成表单的编写,接下来便是接入 JavaScript 进行动态应用了。

4.4.1. 🌟 获取普通表单控件的值

从 JavaScript 的视角来看,表单控件与普通元素没有区别,都是 DOM 节点。

而选中它们之后访问 .value 属性,即可获取表单的值。

html
<input type="text" id="username" value="初始值">
<textarea id="bio">默认文本</textarea>
<select id="city">
  <option selected>北京</option>
</select>

<script>
  const username = document.getElementById('username').value;
  const bio = document.getElementById('bio').value;
  const city = document.getElementById('city').value;
</script>

注意事项

  1. 在 HTML 中所写的 value 只是表示初始值,而 JavaScript 获得的 .value 是用户输入的最新的表单值。
  2. .value 返回的类型是 string(即使设置为 type="number" 也仍然是),如果需要数字类型,则需要手动转换。

4.4.2. ⭐ 文件处理

网页的文件处理比本地复杂很多,需要调用多个高级 API,涉及异步思想。故读者只需先了解基本流程,会模仿即可,之后会详细介绍。

这样设计主要是为了防止文件过大导致页面卡顿。

html
<input type="file" id="file" multiple>

<script>
  const fileInput = document.getElementById('file'); // 获取上传框元素
  fileInput.addEventListener('change', (event) => {
    // 监听文件变化
    const files = event.target.files; // 访问 .files 属性获得文件列表
    for (const file of files) { // for 循环遍历
      const reader = new FileReader(); // 创建文件读取器
      // 注意:FileReader 是异步架构,通过读取完毕触发事件的方式,涉及闭包
      reader.onload = (ev) => {
        const content = ev.target.result;
        console.log(content);
      };
      // 设置完 onload 属性后,调用 readAsText 方法读取文件内容
      // 注意:readAsText 不会返回文件内容,而是读取完毕后触发 onload 事件
      reader.readAsText(file);
    }
  });
</script>

WARNING

现代浏览器为了安全性考虑,不会将路径暴露给 JavaScript,浏览器只能通过文件名区分。所以即使上传的是两个不同文件夹的同名文件,JavaScript 也可能不会认为文件发生了变化。

4.4.3. 🌟 绑定表单控件的事件

表单控件的事件与普通元素的事件类似,都是通过 on 开头的属性绑定。

最重要的是 oninputonchange 事件,它们分别在用户输入任意字符时和用户输入结束时触发。

on 开头系列的属性值都是 JavaScript 代码,当触发时执行其中的内容。

html
<input type="text" id="input" oninput="logInput(event)" onchange="logChange(event)">

<script>
  function logInput(e) {
    console.log('输入事件:', e.target.value);
  }

  function logChange(e) {
    console.log('改变事件:', e.target.value);
  }
</script>

当然也可以通过 addEventListener 绑定事件:

html
<input type="text" id="input">

<script>
  const input = document.getElementById('input');
  input.addEventListener('input', logInput);
  input.addEventListener('change', logChange);

  function logInput(e) {
    console.log('输入事件:', e.target.value);
  }

  function logChange(e) {
    console.log('改变事件:', e.target.value);
  }

event 是哪里来的?

event 是 JavaScript 提供的一个全局变量,它表示当前事件的详细信息。当用户触发事件时,JavaScript 会自动将事件对象传递给事件处理函数,我们可以通过 event 来访问当前事件对象的属性和方法。

4.4.4. ⭐ 数据绑定范式

对于纯前端开发,我们需要将表单数据实时更新至 JavaScript 变量中,然后 JavaScript 变量变化后自动重新计算并更新界面,这时便需要用到数据绑定的范式。

主要的思想是:

  1. 将变量更新封装为 set 开头的函数,用于统一更新数据与界面。
  2. 在表单相应事件中调用 set 函数,实现数据与界面的同步。
html
<input type="number" id="num">
<div id="result"></div>

<script>
  let num = 0;

  document.getElementById('num').addEventListener('input', updateNum);

  function updateNum(ev) {
    const newNum = parseFloat(ev.target.value);
    setNum(newNum);
  }

  function setNum(newNum) {
    num = newNum;
    document.getElementById('result').innerText = `${num} 的立方是 ${num * num * num}`;
  }
</script>

效果:输入数字后,下方 div 实时显示其立方值。

4.4.5. 表单的提交

注意:本小节涉及对象的思想,读者可先暂且了解,后续会详细介绍。现代前端开发中,提交表单并不是必须的,JavaScript 可以绕过表单体系直接针对控件进行操作。

html
<form id="myForm" onsubmit="handleSubmit(event)">
  <input type="text" name="username">
  <button type="submit">提交</button>
</form>

<script>
  function handleSubmit(e) {
    e.preventDefault(); // 阻止默认提交行为
    const formData = new FormData(e.target);
    console.log(Object.fromEntries(formData));
  }
</script>

说明

  • FormData 对象可提取表单数据。
  • Object.fromEntries() 方法将 FormData 对象转换为普通对象。
  • preventDefault() 阻止页面刷新,实现纯前端处理。

4.4.6. 示例:BMI 计算器

以下代码展示了一个完整的 BMI 计算器,包括表单输入、计算和结果展示,是本教程第一个真正意义上具有完整交互功能的微型项目。

html
<form id="bmiForm">
  <label for="height">
    <span>身高 (cm):</span>
    <input type="number" id="height" name="height" required>
  </label>
  <label for="weight">
    <span>体重 (kg):</span>
    <input type="number" id="weight" name="weight">
  </label>

  <button type="submit">计算 BMI</button>

</form>

<div id="result"></div>

<style>
  form {
    border: 2px dashed black;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem;
  }

  #result {
    color: blue;
    text-align: center;
  }
</style>

<script>
  document.getElementById("bmiForm").addEventListener('submit', calculateBMI);

  function calculateBMI(e) {
    e.preventDefault();
    const height = parseFloat(document.getElementById('height').value) / 100;
    const weight = parseFloat(document.getElementById('weight').value);
    const bmi = (weight / (height * height)).toFixed(2);
    document.getElementById('result').innerText = `您的 BMI 值为 ${bmi}`;
  }
</script>

4.5. ⭐ 表单验证

用户的输入永远是不可信的。为了防止用户输入错误的数据,我们需要对表单进行验证。

4.5.1. ⭐ 表单验证的基本概念

表单验证是指,在用户提交表单之前,对表单中的数据进行验证,以确保数据的有效性。

表单验证依靠一系列的验证规则,如:

  1. 必填字段,用来防止用户遗漏必要信息
  2. 格式验证,用来防止用户输入不合格式的数据,如无效的邮箱、手机号等
  3. 范围限制,用来防止用户输入不合理的数据,如超出正常范围的年龄、价格等
  4. 长度限制,用来防止用户输入过长或过短的数据,如姓名、密码等
  5. 其他自定义验证

值得注意的是,本教程暂时只探讨纯前端的项目,而对于服务器参与的项目,表单验证需要在前端与后端两次进行

为什么前端和后端的表单验证不能只验证一次?

  1. 前端验证是为了防止用户无意输入不合规的数据,即时的反馈有助于提升用户体验。
  2. 后端验证是为了防止用户恶意输入不合规的数据,用于维护服务器的安全。
  3. 恶意用户可以通过各类手段(如修改请求头、修改 JavaScript 代码等)绕过前端验证,所以服务器验证尤其不可省略。

4.5.2. 表单验证的传统实现

html
<input type="text" name="username" required minlength="3" maxlength="20">
<input type="number" name="age" min="18" max="99">

属性说明:

属性名作用默认值
required必须填写
minlength最小长度
maxlength最大长度
min最小值
max最大值

这类手段依靠浏览器 HTML 自带的验证,当用户输入不符合规则的数据时,浏览器会自动在相应位置弹出错误提示。

但这种方法存在以下问题:

  1. 浏览器自带的验证规则有限,无法满足某些常见但浏览器不支持的需求。
  2. 无法在 JavaScript 中获取验证结果,无法进行需要计算的自定义验证。

4.5.3. 使用 valibot 进行表单验证

因此,一批前端框架开始提供表单验证的解决方案,其中 valibot 是一个轻量级的表单验证库,支持链式规则定义。

valibot 的验证涉及较高级的思想,如对象思想,读者可以先尝试理解下面的代码。

同时,valibot 作为一个现代的模块,需要通过 import 导入,而不是通过 <script> 标签引入。

含有 import 语句的 JavaScript 文件需要在 <script> 标签中添加 type="module" 属性。

html
<form id="application-form">
  <label>
    用户名:
    <input type="text" autocomplete="username" name="username">
  </label>
  <label>
    邮箱:
    <input type="text" autocomplete="email" name="email">
  </label>
  <button>提交</button>
  <div id="errors"></div>
  <div id="success"></div>
</form>

<style>
  form {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  #errors {
    color: red;
  }

  #success {
    color: green;
  }
</style>

<script type="module">
  import * as v from 'https://unpkg.com/valibot@1.1.0/dist/index.min.js';

  const schema = v.object({
    username: v.pipe(v.string('请输入用户名'), v.minLength(3), v.maxLength(15)),
    email: v.pipe(v.string('请输入邮箱'), v.email())
  });

  const elSuccess = document.getElementById("success");
  const elErrors = document.getElementById("errors");

  document.getElementById('application-form').addEventListener("submit", function (event) {
    event.preventDefault();
    const formData = new FormData(event.target);
    const obj = Object.fromEntries(formData);
    const result = v.safeParse(schema, obj);
    console.log(result);
    if (result.success) {
      elSuccess.textContent = "验证成功!";
      elErrors.textContent = "";
    } else {
      elSuccess.textContent = "";
      elErrors.textContent = "由于以下原因验证失败:";
      for (const issue of result.issues) {
        elErrors.append(document.createElement("br"));
        const key = issue.path.map(p => p.key).join(">")
        elErrors.append(`[${key}] ${issue.message}`);
      }
    }
  })
</script>

说明

  • valibot 是轻量级表单验证库,支持链式规则定义。
  • 虽然是“轻量级”,只是相比于代码更多的 zod 等库而言的。valibot 仍然具有非常齐全的功能和复杂的用法。
  • 读者不需要对自己难以理解其中内容而担心。当前水平只需要先让用户“能用”,然后再考虑如何进行验证等优化。

知识回顾

  1. 表单基础结构与属性

    • <form> 元素
      • 作为表单容器,包裹所有表单控件。
      • methodaction 属性决定表单提交方式(GET/POST)和目标地址。
      • 默认提交行为需通过 event.preventDefault() 阻止,避免页面跳转。
    • <label> 元素
      • 通过 forid 关联控件,或通过嵌套结构隐式绑定。
      • 扩大用户可点击区域,提升交互体验。
    • 表单控件嵌套
      • 控件需包含在 <form> 内部,便于 JavaScript 统一处理。
  2. 表单核心控件 <input> 元素

    • `基本使用
      • type 是最核心属性,决定输入类型(文本、密码、数字、日期、单选、复选、文件等)。
      • autocomplete 支持多种取值(如 usernameemail),引导浏览器智能填充。
      • name 用于表单提交时标识字段。
    • 单选框与复选框
      • 单选框需共享 name 属性,用户只能选择一个选项。
      • 复选框允许多选,需独立设置 name 或使用数组形式提交。
    • 文件上传框
      • accept 限制文件类型,multiple 允许多文件选择。
      • 隐藏域(<input type="hidden">)用于传递不可见的上下文信息(如问卷开始时间)。
  3. 表单其他控件

    • 下拉框(<select>
      • 通过 <option> 定义选项,选中值为 value 属性。
      • 支持 multiple 属性实现多选。
    • 多行文本框(<textarea>
      • rowscols 定义字符数而非像素尺寸。
      • 换行和缩进会被保留,建议在 HTML 中一行写完默认值。
    • 按钮元素(<button>
      • type 决定行为(submitresetbutton)。
      • 自定义操作需绑定 onclick 事件或 addEventListener
  4. 表单 DOM 交互

    • 获取控件值
      • 通过 element.value 获取文本框、下拉框等控件的值。
      • textarea 的默认值通过闭合标签内的文本设置。
    • 文件处理
      • 使用 FileReader API 读取文件内容(readAsText 方法)。
      • 注意 files 属性获取文件列表,而非 value
    • 事件绑定
      • oninput 实时触发(适合搜索),onchange 用户离开输入框后触发(适合验证)。
      • 推荐使用 addEventListener 绑定事件,避免内联事件。
  5. 表单提交与数据绑定

    • 阻止默认提交行为
      • 通过 event.preventDefault() 阻止页面刷新,实现纯前端处理。
    • FormData 对象
      • 提取表单数据并转换为普通对象(Object.fromEntries(formData))。
    • 数据绑定范式
      • 将变量更新封装为 set 函数,实现数据与视图的同步(如计算立方值示例)。
  6. 表单验证

    • HTML 内置验证
      • requiredmin/maxminlength/maxlength 等属性。
      • 浏览器自带验证规则有限,无法满足复杂需求。
    • valibot
      • 轻量级表单验证库,支持链式规则定义。
      • 适用于复杂表单的结构化校验(如用户名长度、邮箱格式)。

课后练习

  1. (单选题)以下哪个属性用于限制输入框的最大值?

    • A. min
    • B. max
    • C. step
    • D. required
  2. (单选题)要创建一个可多选的下拉框,应使用属性 ______。

    • A. multiple
    • B. required
    • C. checked
    • D. selected
  3. (填空题)<input type="number">______ 属性用于设定步长。

  4. (填空题)使用 FileReader 读取文件内容的方法是 ______。

  5. (纠错题)以下代码无法正确获取文件内容的原因是什么?

    html
    <input type="file" id="file">
    <script>
      const file = document.getElementById('file').value;
      console.log(file);
    </script>
  6. (编程题)编写代码:实现一个表单,用户输入 a, b 两个数,下方实时显示 x2+ax+b=0 的方程的解。

    要求:

    1. 允许用户输入任意数字,包括负数和小数。
    2. 使用 HTML 自带验证工具拦截非数字或空输入。当用户输入合法时,计算并显示方程的解。
    3. 若方程没有实数解,则显示 无解;若方程有两个相同的实根,则显示 x1=x2=...;若方程有两个不同的实根,则显示 x1=...; x2=...
    4. 只需支持五位有效数字的数值解,不需要使用根号等符号输出。

    提示:

    1. 可以使用 Math.sqrt() 计算平方根。
    2. 可以对 number 使用 toPrecision() 方法获取指定位数有效数字的字符串。
    3. form 元素也可以直接绑定 input 事件,同时监听内部的所有控件。
参考答案
  1. B. 解析:min 表示最小值,max 表示最大值,step 表示步长,required 表示必须填写。
  2. A. 解析:multiple<select> 元素的属性,用于允许多选;required 表示必填,checked 用于复选框/单选框的默认选中状态,selected 用于下拉框的默认选中项。
  3. step. 解析:step 属性定义输入数值的步长(如 step="0.5" 表示每次增减 0.5)。
  4. readAsText. 解析:FileReaderreadAsText 方法将文件内容读取为字符串,需通过 onload 事件获取结果。
  5. 应通过 .files 属性获取文件对象,而非 .value
  6. 参考代码实现
    html
    <form id="quadratic-form">
      <label>
        a:
        <input type="number" id="a" name="a" step="any" s required>
      </label>
      <label>
        b:
        <input type="number" id="b" name="b" step="any" s required>
      </label>
      <div id="result"></div>
    </form>
    
    <style>
      form {
        display: flex;
        flex-direction: column;
        align-items: center;
      }
    
      #result {
        font-weight: bold;
        font-size: 1.25rem;
        font-family: 'Cambria Math', '楷体', 'sans-serif';
      }
    
      form input {
        width: 5rem;
      }
    </style>
    
    <script>
      document.getElementById("quadratic-form").addEventListener("input", solveEquation);
    
      function solveEquation() {
        const a = parseFloat(document.getElementById("a").value);
        const b = parseFloat(document.getElementById("b").value);
        const result = document.getElementById("result");
        let output = "";
    
        if (isNaN(a) || isNaN(b)) {
          result.textContent = "请输入有效数字";
          return;
        }
    
        const delta = a * a - 4 * b;
        if (delta < 0) {
          output = "无解";
        } else if (delta === 0) {
          const root = (-a) / 2;
          output = `x1=x2=${root.toPrecision(5)}`;
        } else {
          const sqrtDelta = Math.sqrt(delta);
          const x1 = (-a + sqrtDelta) / 2;
          const x2 = (-a - sqrtDelta) / 2;
          output = `x1=${x1.toPrecision(5)}; x2=${x2.toPrecision(5)}`;
        }
    
        result.textContent = output;
      }
     </script>
    解析
    • 表单验证:通过 type="number"required 确保输入合法。
    • 计算逻辑
      • 判别式 delta = a² - 4b 决定解的类型。
      • delta < 0:无实数解。
      • delta === 0:唯一解 x = -a/2
      • delta > 0:两个不同解。
    • 精度控制:使用 toPrecision(5) 保留五位有效数字。
    • 实时更新:通过 input 事件监听输入变化,即时计算并更新结果。

拓展阅读

  1. MDN 表单文档
  2. valibot 官方文档
  3. 前端表单验证实践
  4. FileReader API 使用指南
  5. Web 表单设计最佳实践

Built by Vitepress | Apache 2.0 Licensed