网站已经能展示路线和行程,现在要让访客填写报名信息。表单不只是几个输入框:每个控件都要有名称、标签和明确的数据类型,提交后服务器才能理解它。

所有需要一起提交的控件放在 <form> 中:
<form action="/registrations" method="post">
<!-- 输入控件放在这里 -->
</form>action 是接收数据的地址,method 是请求方式。常见的两种方式是:
get:数据通常出现在 URL 查询字符串里,适合搜索、筛选等可重复访问的操作。post:数据放在请求体中,适合创建报名、留言等会改变服务器数据的操作。我们现在只写静态页面,没有接收数据的服务器。练习时可以先写 action="",但要知道这不会真正保存报名。
浏览器端的 required、minlength 等验证能改善填写体验,却不能替代服务器验证。真实项目必须在服务器再次检查输入,也不能用普通表单明文处理密码之外的敏感秘密。
一个完整的文本输入项至少包括 label 和 input:
<p>
<label for="full-name">姓名</label>
<input id="full-name" name="fullName" type="text" autocomplete="name" required>
</p>label 的 for 与 input 的 id 必须完全相同。这样点击文字也能聚焦输入框,屏幕阅读器也知道这个输入框的名称。
name 是提交给服务器的字段名。只有 id 没有 name,控件值通常不会进入表单提交数据。
不同的 type 会带来不同的键盘、基础验证和浏览器辅助:
<p>
<label for="email">邮箱</label>
<input id="email" name="email" type="email" autocomplete="email" required>
</p>
<p>
<label for="phone">手机号码</label>
<input
placeholder 只能提供格式提示,不能代替 label。用户输入后占位文字就会消失。
一组单选按钮共享同一个 name,每项有不同的 value:
<fieldset>
<legend>选择路线</legend>
<label>
<input type="radio" name="route" value="canal" required>
运河慢行线
</label>
<label>
<input type="radio" name="route" value="old-town"
fieldset 把相关控件组成一组,legend 是组名。复选框允许同时选择多项:
<fieldset>
<legend>需要的协助</legend>
<label>
<input type="checkbox" name="support" value="slow-pace">
需要慢速队伍
</label>
<label>
<input type="checkbox" name="support" value="large-print">
选项较多、只需选择一项时,可以用 select:
<label for="group-size">同行人数</label>
<select id="group-size" name="groupSize" required>
<option value="">请选择</option>
<option value="1">1 人</option>
<option value="2">2 人</option>
<option
<p>
<label for="note">补充说明</label>
<textarea id="note" name="note" rows="5" maxlength="300"></textarea>
</p>textarea 的默认内容写在开始和结束标签之间,不使用 value 属性。这里留空即可。
<button type="submit">提交报名</button>
<button type="reset">清空表单</button>表单里的按钮应明确写 type。submit 提交表单,reset 会一次清空很多内容,真实产品里通常要谨慎使用,避免误操作。
把下面的主体代码放入 contact.html:
<body>
<ul>
<li><a href="index.html">首页</a></li>
<li><a href="routes.html">路线</a></li>
<li><a href="schedule.html">行程</a></li>
<li
不填写必填项直接提交,观察浏览器提示;再输入格式错误的邮箱。最后打开开发者工具,检查每个标签的 for 是否能找到同名 id。下一章我们会把四个页面的结构从“内容堆在 body 里”升级为清楚的语义区块。