尚品优品技术编辑 · 原创教程 · 更新于2026年10月23日 · 约7分钟阅读
一个菜单能够点开,一个表单能够显示成功提示,只完成了交互的一部分。用户还会用键盘、重复点击、填错邮箱,或在网络较慢时等待。这篇教程从状态出发搭建菜单和表单,让每个动作都有对应反馈。
一、把折叠菜单写成按钮与内容的关系
菜单按钮使用 button,明确写出它控制哪个区域,并用 aria-expanded 表示当前是否展开。被控制的区域包含正常链接。这样可以将“打开菜单”与“访问页面”两类行为分清楚。
<button id="menu-toggle" aria-controls="site-nav"
aria-expanded="false">菜单</button>
<nav id="site-nav" hidden>
<a href="/projects">项目案例</a>
<a href="/blog">博客</a>
</nav>按钮激活时同步更新展开状态与 hidden。原生按钮已经支持常见键盘激活方式,不必再把一个 div 模拟成按钮。对于覆盖整个屏幕的复杂菜单,还需要单独考虑焦点移动、关闭操作和背景内容是否可交互。
const toggle = document.querySelector('#menu-toggle');
const nav = document.querySelector('#site-nav');
toggle.addEventListener('click', () => {
const open = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', String(!open));
nav.hidden = open;
});二、先把表单字段与标签写对
每个字段都需要可理解的标签。邮箱使用 email 类型,电话可以使用 tel,必填内容使用 required。不要把所有规则都塞进一段 JavaScript;浏览器已有的输入能力可以作为第一层反馈。占位文字适合提供例子,不能取代字段标签。
<label for="email">联系邮箱</label>
<input id="email" name="email" type="email"
autocomplete="email" required>
<p id="form-status" role="status"></p>三、明确提交的五种状态
把状态列成:未提交、填写有误、正在发送、发送成功、发送失败。正在发送时更改按钮文字并避免重复请求;成功时说明用户下一步可以做什么;失败时保留已填写内容,给出重试入口。不要只把按钮变灰,却没有任何解释。
前端校验帮助使用者及时修正输入,服务器仍需对收到的数据进行验证。前端显示成功,也应对应真实的保存或发送结果。如果当前只是本地演示,就应说明数据保存位置,不能把演示提示写成邮件已经送达。
四、把错误提示放到具体字段附近
错误信息应说明怎样改,例如“请输入包含 @ 的有效邮箱地址”,而不是只写“输入错误”。如果使用自定义提示,需要让提示与字段关联;在确实发现无效输入之后,再设置无效状态。不要刚进入页面就把所有空字段标红。
提交失败后不应清空项目需求。较长的表单可在顶部提供错误概览,字段附近保留具体说明。检查读屏与键盘焦点时,确认用户能从概览找到问题字段,也能理解状态变化。
五、用真实操作走完异常路径
测试时先空提交,再输入错误邮箱,再输入完整内容;模拟接口失败后重试,检查按钮是否恢复可用;快速连续点击,检查是否出现重复记录。菜单则测试 Tab、Enter、Space 和关闭后的焦点位置。
最后从首页进入详情,再打开菜单前往博客,连续后退和前进。很多交互故障出现在第二次进入页面,或者跨尺寸切换之后。保留原有动画时,也要让状态控制只有一个来源,避免多个监听器同时开关菜单。
练习任务:给一个联系表单制作成功与失败两种模拟返回,记录每一步的按钮文字、字段内容与状态提示。能清楚解释每个状态,通常也更容易把它实现稳定。
参考资料与继续阅读
本文由尚品优品整理编写,示例用于学习,进一步细节可查阅以下官方资料。

