第18章 表单、数据验证与常用Web API

从用户输入到数据存储 | 课型:实习课 | 建议课时:3节

🏠 返回总目录 / 第18章

🎯 学习目标

知识目标

  • 掌握表单标签与输入类型
  • 掌握JS验证与HTML5原生验证
  • 掌握fetch请求与JSON处理
  • 掌握localStorage本地存储
  • 了解正则与异步编程

能力目标

  • 能制作带完整验证的报名表单
  • 能用fetch提交数据并处理响应
  • 能用localStorage持久化数据
  • 能编写基础正则表达式

德育目标

  • 培养数据安全意识:永不信任用户输入
  • 理解表单设计中的用户体验关怀

教学重点

  • 输入类型与required/pattern
  • 验证三件套:格式/必填/范围
  • fetch+JSON基本流程

教学难点

  • 正则表达式的构造
  • Promise与async/await异步思维

💻 实例演示1个实例 · 可在线运行

▶ 竞赛报名表单(带验证与存储)

姓名/手机/邮箱验证+localStorage自动保存草稿+提交成功反馈。

新窗口运行 下载源码

操作步骤

  1. 打开demo1.html,直接点提交观察四项错误提示
  2. 输入错误手机号观察即时反馈
  3. 正确填写后刷新页面——草稿自动恢复(localStorage)
  4. 提交成功后到开发者工具Application面板查看存储
  5. 修改手机号正则为/^1[3-9]\d{9}$/更严格版本

效果观察点

  • novalidate关闭原生验证改用JS统一控制
  • 错误提示逐字段显示不跳转
  • input事件实时存草稿
查看核心代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>竞赛报名表</title>
<style>
  body { font-family: "Microsoft YaHei", sans-serif; background: #f2f6f8; margin: 0;
         display: flex; justify-content: center; padding: 30px 16px; }
  form { background: #fff; padding: 28px; border-radius: 14px; width: 420px;
         box-shadow: 0 6px 24px rgba(18,58,82,.1); }
  h1 { font-size: 20px; color: #123a52; margin: 0 0 18px; }
  label { display: block; margin: 14px 0 6px; font-size: 14px; }
  input, select { width: 100%; padding: 10px; border: 2px solid #d7e3ea; border-radius: 8px;
                  font-size: 15px; box-sizing: border-box; }
  input:focus { border-color: #0f8c7f; outline: none; }
  .err { color: #c05a1e; font-size: 13px; min-height: 18px; margin-top: 4px; }
  button { margin-top: 18px; width: 100%; padding: 12px; border: none; border-radius: 8px;
           background: #0f8c7f; color: #fff; font-size: 16px; cursor: pointer; }
  #ok { display: none; margin-top: 14px; background: #e7f7f4; color: #0f8c7f;
        padding: 10px; border-radius: 8px; text-align: center; }
</style>
</head>
<body>
  <form id="reg" novalidate>
    <h1>第47届世赛选拔赛报名表</h1>
    <label>姓名</label><input id="name" placeholder="请输入真实姓名"><div class="err" id="e-name"></div>
    <label>手机号</label><input id="phone" placeholder="11位手机号"><div class="err" id="e-phone"></div>
    <label>邮箱</label><input id="email" placeholder="name@example.com"><div class="err" id="e-email"></div>
    <label>参赛模块</label>
    <select id="module"><option value="">请选择</option><option>模块A 网页元素开发</option><option>模块B 页面设计</option></select>
    <div class="err" id="e-module"></div>
    <button type="submit">提交报名</button>
    <div id="ok">✅ 报名成功!数据已保存到localStorage</div>
  </form>
<script>
  const $ = id => document.getElementById(id);
  const rules = {
    name:  [v => v.trim().length >= 2, '姓名至少2个字符'],
    phone: [v => /^1\d{10}$/.test(v), '手机号应为1开头的11位数字'],
    email: [v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v), '邮箱格式不正确'],
    module:[v => v !== '', '请选择参赛模块']
  };
  // 草稿自动保存
  ['name','phone','email','module'].forEach(id => {
    $(id).value = localStorage.getItem('reg-' + id) || '';
    $(id).addEventListener('input', () => localStorage.setItem('reg-' + id, $(id).value));
  });
  $('reg').addEventListener('submit', e => {
    e.preventDefault();
    let pass = true;
    for (const id in rules) {
      const [test, msg] = rules[id];
      const ok = test($(id).value);
      $('e-' + id).textContent = ok ? '' : '⚠ ' + msg;
      if (!ok) pass = false;
    }
    if (pass) { $('ok').style.display = 'block'; console.log('提交数据:', JSON.stringify({
      name: $('name').value, phone: $('phone').value, email: $('email').value, module: $('module').value })); }
  });
</script>
</body>
</html>

📋 实习任务书

任务目标:独立完成带验证、存储的竞赛报名表

任务内容与评分
任务1:搭建四字段表单结构(15分)
任务2:编写手机号与邮箱正则验证(25分)
任务3:错误提示逐字段友好显示(20分)
任务4:localStorage草稿自动保存(20分)
任务5:用fetch将数据POST到测试接口(20分)

自我评价

📚 课后作业与预习