D03 后台表单页
摘要:后台页面由两类结构反复组合:录入表单与数据列表。它们对 HTML 的要求与展示页不同——标签必须准确关联、错误必须可定位、操作必须能用键盘完成。本文给出一个「任务录入 + 任务列表」页面的完整结构,含六类控件、分组标题、错误汇总与表格操作列。
关键词:后台表单,字段分组,数据列表,操作列,键盘操作
所属:frontend/html / D-实战 | 上一篇:D02-文章详情页 | 下一篇:无 | 返回:D00-总结
1 引言
后台页面的使用者每天重复同一套操作,任何一次多余点击都会被放大。结构层面的优化只有三点:字段按语义分组、标签与控件一一关联、列表与表单在同一页面内形成闭环。
本文给出一个可直接改造成真实页面的骨架。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 表单 | 六个字段、三个分组、必填与错误汇总 | 接口提交与状态管理 |
| 列表 | 表格结构、操作列、空状态 | 分页、排序与筛选(依赖脚本) |
| 可用性 | 键盘操作、错误播报、autocomplete |
视觉密度与主题 |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | A02 的本地服务器 | 能访问 localhost |
| 前置 | A07、A06 | 表单与表格已掌握 |
| 前置 | C02 | 校验分层与错误提示已掌握 |
| 文件 | site/admin.html |
与 D01 站点同目录 |
3 原理与实现
3.1 页面由「录入 → 列表 → 回填」闭环构成
后台页面的三段关系固定,缺一段就需要在页面间跳转,如图 3-1 所示。
flowchart LR
A["录入表单"] --> B["校验与错误汇总"]
B -->|"通过"| C["提交"]
B -->|"不通过"| A
C --> D["数据列表"]
D --> E["行内操作:编辑 / 删除"]
E --> A
图 3-1 录入与列表的闭环
图上可见:编辑操作把列表数据带回表单,因此两处的字段名必须一致,否则回填会丢字段。
3.2 完整结构
<!-- 运行环境:Chrome 130+;site/admin.html -->
<main>
<h1>任务管理</h1>
<form id="task-form" novalidate>
<fieldset>
<legend>基本信息</legend>
<p>
<label for="title">任务标题</label>
<input id="title" name="title" type="text" required maxlength="60" autocomplete="off" aria-describedby="title-error" />
<span id="title-error" role="alert"></span>
</p>
<p>
<label for="owner">负责人</label>
<input id="owner" name="owner" type="text" required autocomplete="name" />
</p>
</fieldset>
<fieldset>
<legend>计划</legend>
<p>
<label for="start">开始日期</label>
<input id="start" name="start" type="date" required />
</p>
<p>
<label for="level">优先级</label>
<select id="level" name="level">
<option value="low">低</option>
<option value="normal" selected>普通</option>
<option value="high">高</option>
</select>
</p>
<fieldset>
<legend>协作方式</legend>
<label><input type="radio" name="mode" value="solo" checked /> 独立</label>
<label><input type="radio" name="mode" value="pair" /> 结对</label>
</fieldset>
</fieldset>
<fieldset>
<legend>备注</legend>
<p>
<label for="note">说明</label>
<textarea id="note" name="note" rows="3" maxlength="200"></textarea>
</p>
<p aria-live="polite" id="form-summary"></p>
</fieldset>
<p>
<button type="submit">保存</button>
<button type="reset">清空</button>
</p>
</form>
<section>
<h2>任务列表</h2>
<table>
<caption>最近 3 条任务</caption>
<thead>
<tr>
<th scope="col">标题</th>
<th scope="col">负责人</th>
<th scope="col">优先级</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">补齐文档链接校验</th>
<td>李明</td>
<td>高</td>
<td><a href="#task-form">编辑</a></td>
</tr>
<tr>
<th scope="row">统一图片尺寸</th>
<td>王芳</td>
<td>普通</td>
<td><a href="#task-form">编辑</a></td>
</tr>
</tbody>
</table>
<p>共 2 条记录。</p>
</section>
</main>
<script>
const form = document.getElementById('task-form');
const summary = document.getElementById('form-summary');
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) {
const invalid = form.querySelectorAll(':invalid');
summary.textContent = `共 ${invalid.length} 项未填写完整。`;
form.querySelector(':invalid').focus();
return;
}
summary.textContent = '已保存。';
});
</script>
表单与列表的结构要点见表 3-1。
| 构件 | 要求 | 常见问题 |
|---|---|---|
| 字段分组 | fieldset + legend,每组不超过 5 个字段 |
全表单一个 legend,失去分组意义 |
| 标签 | 每个控件都有 label for |
用占位文字代替标签 |
| 必填 | required + 星号说明 |
只在标签后加星号,不写约束 |
| 错误汇总 | aria-live="polite" 区域,报告数量 |
错误只标红,无文本 |
| 表格 | caption + thead + th scope |
表头用 td 加粗 |
| 操作列 | 每行独立操作,锚点或按钮 | 操作与行数据无对应关系 |
| 空状态 | 无数据时给出一行说明 | 空表格仅剩表头 |
键盘可达性检查见表 3-2。
| 操作 | 期望行为 | 检查方式 |
|---|---|---|
| 字段间移动 | Tab 按视觉顺序前进 |
逐次按 Tab 记录顺序 |
| 单选组切换 | 方向键在同一组内切换 | 聚焦后按左右键 |
| 提交 | 聚焦按钮后回车即可提交 | 不使用鼠标 |
| 跳回表单 | 列表中「编辑」可定位到表单首字段 | 点击后检查焦点位置 |
4 验证与讨论
- 留空标题提交,确认焦点跳到标题字段,汇总区显示未完成项数。
- 仅用键盘完成一次录入:
Tab遍历、方向键选优先级、回车提交。 - 点击列表中的「编辑」,确认焦点落到表单标题字段。
- 用朗读软件检查:
legend是否被播报、错误文本是否与字段关联。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 分组播报 | 进入分组时播报组标题 | legend |
| 错误定位 | 首个错误字段获得焦点 | 4 节步骤 1 |
| 列表语义 | 播报表头与行标题 | th scope |
| 回填闭环 | 编辑后焦点在表单 | 锚点指向 #task-form |
| 纯键盘可用 | 全部操作可完成 | 表 3-2 |
本项目五项取舍见表 4-1。
| 决策点 | 采用方案 | 备选方案 | 不选的理由 |
|---|---|---|---|
| 表单与列表 | 同页上下排布 | 弹层内编辑 | 同页减少焦点管理复杂度 |
| 字段分组 | 三个 fieldset |
无分组平铺 | 分组利于朗读与视觉扫描 |
| 错误提示 | 字段旁文本 + 顶部汇总 | 仅弹窗提示 | 弹窗关闭后无法回看哪项出错 |
| 优先级 | select |
单选组 | 选项多于三个时下拉更省空间 |
| 操作列 | 锚点链接 | 按钮 + 脚本 | 锚点无需脚本即可完成定位 |
讨论与局限。 本页未包含分页与排序,真实列表需要脚本或服务端支持。另同页编辑在数据量大时会导致页面过长,届时应改为独立编辑页,并保留返回列表的导航。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 点标签文字不聚焦控件 | for 与 id 不一致 |
逐对核对 |
| 提交后页面刷新丢失输入 | 未阻止默认提交行为 | 脚本中 preventDefault() |
| 单选组可同时选中 | 组内 name 不同 |
统一 name,用 value 区分 |
| 错误提示不播报 | 汇总区缺少 aria-live |
补 aria-live="polite" |
| 长表单提交后找不到错误位置 | 未聚焦首个无效字段 | focus() 到 :invalid 元素 |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 后台页面由录入、列表、回填三段闭环构成 | 图 3-1 |
| 2 | 字段分组与标签关联决定可用性下限 | 表 3-1 |
| 3 | 表格必须给表头语义,操作列需与行数据对应 | 表 3-1 |
| 4 | 纯键盘走通一次是必做验收 | 表 3-2 |
参考文献
[1] MDN Web Docs · fieldset 与 legend,https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/fieldset(访问于 2026-10-29)
[2] W3C · 表单无障碍实践,https://www.w3.org/WAI/tutorials/forms/(访问于 2026-10-29)

评论 0
还没有评论,快来抢沙发吧~