写表单这件事做过几个项目的人大概都有同一种体验一个页面上十几个input排下来代码能跑、需求也能过但过三个月自己回头看第一反应是这个字段到底是干什么的。标题里说的input 加注释表面上是个编码规范的小问题实际上是前端表单可维护性的一道分水岭。它至少包含三层意思一是给代码写 HTML 注释让接手的人知道这个框的来龙去脉二是给用户写可见的说明文字也就是 label 和辅助提示三是给辅助设备和自动化测试写语义注释比如aria-describedby、autocomplete这类。这三层经常被混为一谈最后落成一堆 placeholder 一删就没人看得懂的页面。下面我按自己的理解把 HTML 常见 input 类型和对应的注释写法系统捋一遍从类型边界讲到注释落位再讲几种真实踩过的坑适合刚接手表单模块的前端、需要维护老后台的同学以及写页面时会纠结注释写哪儿的人参考。1. 把 input 家族按输入意图重新分一遍1.1 文本与数值输入类的边界其实没那么模糊很多人对 input 类型的理解停留在能用就行typetext一把梭反正后台再校验。但浏览器对不同类型的处理差异远比想象中大移动端键盘布局、自动填充行为、内置校验、输入法处理方式全都不一样。typetext是最宽的那个什么都不管但它在移动端唤起的是通用键盘用户填手机号得自己切数字键盘这就是体验损耗。typepassword负责遮蔽显示但要注意它只是视觉遮蔽不做任何加密值还是明文在 DOM 里。typeemail在支持 HTML5 校验的浏览器里会要求格式带提交时若不合规会直接拦截并弹提示这条内置校验在桌面端挺好用但移动端键盘会变成带和.的布局填非邮箱内容时反而碍事。typetel是个纯粹的键盘类型声明它不做任何格式校验因为全球电话号码格式差异太大标准干脆不管。这个特性经常被误用成电话校验实际校验该用pattern。typeurl会做协议校验输入www.example.com这种不带协议的会被判不合规这点在注释里必须写明否则用户会一脸懵。typenumber是坑最多的一个后面会专门讲。typesearch在部分浏览器里输入框右侧会带一个清除按钮样式还不受控。我的经验是注释里必须标注为什么选这个类型否则下一个人接手会顺手把它改回 text把移动端键盘优化一并干掉。1.2 选择、提交与文件类的搭配套路选择类里radio和checkbox的核心区别不在视觉而在语义radio是同一name下的互斥组checkbox是独立的多选。这意味着一组 radio 天然构成一组业务上可以整体加一句注释说明以下三项互斥默认选中第二项而 checkbox 是分散的每一项都得单独说明。submit、reset、button这三种提交按钮在现代项目里基本被button typebutton JS 替代了但老代码里还有。这里有个经典坑form内的button如果不写type默认就是submit点一下整页提交很多人调试时莫名其妙页面刷新就是这个原因。注释里写一句此处必须是 typebutton能省下后来人半小时。file类型的注释重点不在类型本身而在accept和multiple两个属性以及文件不是立即上传这个认知差。用户选完文件文件还在本地input.files里拿到的是 File 对象真正的上传得靠 JS 或表单提交。typecolor用得少但在后台配置主题色的场景里很实用注释里要说明它的值格式固定为#rrggbb小写十六进制。1.3 日期时间与隐藏域这些看起来简单的类型日期时间家族包括date、time、datetime-local、month、week它们的值格式都是 ISO 8601 的变体比如date是2024-05-01datetime-local是2024-05-01T13:30。这几个类型在三件事上差异巨大浏览器原生控件的支持程度、值的格式、时区语义。datetime-local不带时区date也不带如果你直接把它扔给后端当 UTC 用跨时区就出问题。这个必须写在注释里不然线上数据会静默错一个小时。range是滑块值永远是字符串形式的数字step不设默认是 1设成 0.1 才能拖出小数。hidden是最特殊的它不出现在界面上但在 DOM 里明晃晃地躺着任何人打开开发者工具都能改。我见过太多人把用户 ID和价格塞进 hidden 直接信任这是典型的安全认知错误。hidden 的注释必须写明此值仅用于前端流程串联后端必须重新校验。类型移动端键盘内置校验值格式注释重点text通用无任意为什么不用专用类型tel数字键盘无任意格式规则要写清email带 键盘有含 是否允许内部域名number数字键盘有范围数字字符串精度与负号策略date日期选择器有YYYY-MM-DD时区语义range滑块无字符串数字step 与单位hidden无无任意不可信任后端必校验2. 注释到底该写在哪一层代码注释、label 与语义描述的三重分工2.1 HTML 注释只解决给同事看解决不了给用户看先说最直接的一层!-- --。它的作用域只有源码用户在页面上一个字也看不到。这决定了它的定位——写给维护者看的背景信息不是写给用户的说明。所以合理的 HTML 注释应该承载这类内容这个字段的业务来源是什么、为什么要做这个限制、历史上改过什么、和后端哪个接口的哪个参数对应。一个不好的写法是!-- 用户名输入框 --这种注释等于把标签名翻译了一遍零信息量。好的写法应该带上下文比如!-- 注册来源渠道由投放页通过 URL 参数带入隐藏展示用户不可见 --。差别就在于是不是补充了代码本身表达不了的东西。有个技术细节必须提前说清楚HTML 注释不能写在标签内部。下面这种写法是无效的注释会被当成属性解析标签结构直接崩掉!-- 错误写法注释插在属性中间 -- input typetext !-- 用户名 -- nameusername正确做法是把注释放在元素上一行或者放在包裹元素的外部。另外 HTML 注释不支持嵌套注释内容里也不能出现连续的--否则解析器会提前闭合。还有一点容易被忽略HTML 注释会被完整下载到客户端源码里写着这里临时写死密码 123456等于公开。所有注释交给用户之前都得当作明文看待。2.2 label 才是输入框的法定注释用户真正能看到的注释是label。它解决两个问题视觉上说明这个框要填什么交互上建立点击关联——点文字能聚焦到输入框这对复选框尤其重要一个 16px 的小方块点击区域太难受了。关联方式有两种for属性指向id是最通用的也最推荐。另一套是把 input 嵌套在 label 内部不用写for但样式控制会更麻烦尤其是需要绝对定位图标的时候。我的建议是统一用for id团队里不要两种混用。!-- 推荐显式 for/id 关联 -- label forcontact-phone联系电话/label input idcontact-phone namephone typetel inputmodenumeric !-- 次选包裹式样式受限 -- label 联系电话 input namephone typetel /label这里有个反直觉的结论placeholder不能替代 label。placeholder 是输入框为空时的提示一旦输入内容就消失用户填到一半回头看就忘了这栏是什么。多家无障碍规范和主流可用性研究都明确指出这一点的危害尤其是记忆负担较重的长表单。而且 placeholder 的对比度通常很低在强光环境下几乎看不见。2.3 aria-describedby 和 fieldset 补上复杂说明的位置label 只能承载一句短标题但很多字段需要更长的说明密码至少 8 位需包含大小写字母和数字格式为 YYYY-MM-DD请勿填写未来日期。这些内容放在 label 里会把版面撑乱放在 placeholder 里又会消失。标准解法是用aria-describedby指向一段说明文本视觉上可以把这段说明放在输入框下方同时让读屏软件在聚焦时自动朗读出来。这相当于给输入框挂了一条补充注释而且对屏幕阅读器用户和普通用户是同一份内容不用维护两套。label forpwd登录密码/label input idpwd namepassword typepassword aria-describedbypwd-tip autocompletecurrent-password p idpwd-tip8 到 20 位需同时包含字母和数字。/p再往上还有fieldsetlegend用于把一组相关控件圈成一个整体比如收货地址下面的一整片字段或者一组互斥的单选项。legend 就是这一组的组注释读屏时用户会听到收货地址组合框能立刻建立上下文。表单字段一多这层结构是区分能跑的表单和好用的表单的关键。3. 按类型拆解每种 input 该配什么样的注释3.1 文本类字段把格式约定写死在注释里text、password、email、tel、url这几个是最常见的注释的核心任务是明确格式边界和异常情况。比如手机号字段注释至少要写清楚是否允许带区号、是否允许带空格和横线、后端存的是原始串还是归一化后的串。这三条不写前端校验规则和后端存储对不上测试环境永远能过、线上永远出错。autocomplete属性值得单独提它是浏览器自动填充的依据。登录表单该写current-password注册表单写new-password用户名写username邮箱写email手机号写tel。不写或者写错浏览器会瞎猜把密码填进搜索框的事情就这么来的。这个属性本身就是一种给机器的注释写对了能直接减少用户的输入成本。password的注释重点是是否允许粘贴、是否需要强弱提示、要不要提供明文切换。现在越来越多的产品默认允许粘贴密码因为禁用粘贴反而逼着用户设更弱的密码。这些取舍应当写进注释让接手的人知道这是权衡后的结果而不是漏掉了。3.2 数值与日期类把看起来能输和实际能被接受区分开typenumber是坑王必须专门写注释。它的三个反直觉行为一是在部分浏览器里允许输入e科学计数法、、-、.键盘上这些键并没有被禁二是value拿到的是字符串空值是而不是null三是step不匹配时表单会静默校验失败用户点提交像没反应一样。加上 iOS 上滚轮误触改变值的体验问题很多团队最终选择typetext inputmodenumeric配 JS 校验。!-- 金额输入 1. 使用 text inputmode 而非 number避免 e/负号/滚轮误触 2. 前端仅做格式提示精度校验以后端为准保留两位小数 3. 单位元不含货币符号 -- input idamount nameamount typetext inputmodedecimal aria-describedbyamount-tip placeholder0.00 p idamount-tip请输入金额最多两位小数。/p日期类的注释重点是时区和格式。typedate只返回YYYY-MM-DD没有时间也没有时区。如果业务上这个是北京时间某天零点那注释里必须写清楚后端按哪个时区解析也得对得上。min和max能限制可选范围但用户手输仍然能越过前端校验不能省。range的注释要写清楚步长和单位比如音量 0-100步长 5显示值需自行渲染。3.3 选择、文件与隐藏域注释的侧重点完全不同一组radio的注释应该写在组级别而不是每一项上。用fieldset包起来legend写组名然后一句 HTML 注释说明默认选中项和互斥逻辑。checkbox通常是独立开关每一项都要能单独说清楚含义尤其是记住我同意协议这类涉及流程的开关注释里要说清楚勾选后会发生什么。file的注释重点是accept的取值和大小限制。acceptimage/png,image/jpeg只是给文件选择器一个过滤提示用户仍然可以在某些系统里选到别的类型真正的类型校验必须读文件头。大小限制也没法用 HTML 属性做只能靠 JS 读file.size。这两条不写在注释里下一个人会以为accept就是安全边界。hidden域是注释价值最高的地方。它界面上看不见但参与提交半年后没人敢删。所以每个 hidden 都应该有一句注释说明来源和用途比如由活动页写入标识投放渠道后端用于归因统计。如果某个 hidden 的注释写不出来那它大概率可以删掉。4. 几个真实的排查现场注释缺失会带来什么后果4.1 placeholder 当 label用户输入到一半就失忆早年间做过一个信息登记页十几个字段全靠 placeholder 提示视觉上确实干净。上线后收到反馈说填到后面忘记前面填的是啥。原因很简单一旦输入内容提示就消失了用户想核对只能清空重填。更糟的是读屏软件对 placeholder 的支持很不一致有些场景下根本读不出来。这个问题的修复成本极低——补 label样式上可以用视觉隐藏的方式处理给 label 加一个只对读屏可见的类既保持版面干净又不丢语义。但排查过程很费劲因为从代码上看页面有提示文字啊问题不在功能而在认知。这类坑的教训是任何临时省掉 label的决定都应该在代码里留一行注释说明这是有意为之还是待补否则下一轮迭代时会一直悬着。4.2number写了只能输数字实际还能输入 e 和负号某次线上出现提交金额为1e5的记录后端解析报错。排查时发现输入框是typenumber前后端都以为它天然只接受数字所以没做额外过滤。实测下来在桌面浏览器里敲1e5完全能输进去value就是字符串1e5表单校验也不拦。修复方案是换成typetext inputmodedecimal输入时用input事件过滤非法字符同时在提交前做一次正则校验。这个案例的通用结论是浏览器内置行为不等于业务约束所有我以为它不让输的地方都得实测一遍再下结论。而这种以为恰恰是最该写进注释的东西——把实测结论写下来后面的人就不会再踩第二次。4.3 hidden 域没注释重构时谁也不敢动有个老项目的表单里躺着六个 hidden名字是t、s、c1、c2这种。没人知道它们是干什么的但因为删了可能出事就一直留着。后来查日志才发现其中三个已经废弃两年另三个是渠道标识只在一个投放页用到。这件事之后我们定了个规矩新增 hidden 必须写三行注释——来源、用途、失效条件。写不出来的就不加。这个成本几乎为零但省下的是未来每一次重构时的犹豫。类似的还有name属性的命名尽量用具语义的英文别用缩写namechannel永远比namec1好维护。5. 把注释落成团队能执行的规范而不是靠自觉5.1 一套可以直接抄的注释书写顺序我把表单字段的注释拆成固定四段写在元素上方顺序固定谁写都一样用途这个字段收集什么对应后端哪个字段或接口参数。约束格式、长度、取值范围、是否必填以及这些约束在哪一层校验。取舍为什么选这个 type、为什么不做某个功能比如刻意不加实时校验避免打断输入。陷阱实测发现的浏览器差异或历史遗留问题。!-- 用途用户昵称对应 /api/profile 的 nickname 字段 约束2-16 字符允许中英文数字前后端均校验 取舍用 text 而非专用类型不做实时校验失焦时提示 陷阱iOS 下中文输入法组合期间会触发多次 input需防抖 -- label fornickname昵称/label input idnickname namenickname typetext maxlength16 autocompletenickname aria-describedbynickname-tip p idnickname-tip2 到 16 个字符支持中英文和数字。/p这套顺序的价值在于可检索。团队里所有人注释格式一致搜陷阱就能把所有已知的浏览器坑翻出来比翻文档快得多。第二段约束尤其重要它明确了校验责任在前后端哪一侧避免出现都以为对方校验了的经典事故。5.2 用自查清单和工具兜底别指望人记住规范写在文档里没人看写在 checklist 里才会被执行。我们现在的表单自查项大概这么几条提交代码前过一遍每个 input 是否都有可关联的 label视觉隐藏也算每个 hidden 是否有来源 用途 失效条件的注释每个用了非 text 类型的字段注释里是否说明了选型理由每个带accept或maxlength的字段注释里是否标注了这只是提示不是校验placeholder 是否只用于举例而不是承载唯一说明工具层面能自动化的就别靠人。ESLint 配jsx-a11y/label-has-associated-control能强制检查 label 关联模板项目里配 htmlhint 可以检查重复 id 和缺失的alt/for。至于注释质量机器判断不了但可以用一个简单的规则兜底改动 input 相关代码时必须同时检查上方注释是否仍然正确。注释过期比没有注释更危险因为它会误导人。还有一条不算规范但很实用的习惯写注释的时候把当时为什么这么选写下来而不是只写现在是什么。前者是后人真正需要的后者代码自己会说。我个人在几个表单密集的项目里反复验证下来最有性价比的一件事就是给每个非默认类型的 input 补一句选型理由。它花不了几分钟但在移动端键盘、自动填充、内置校验这三块能省掉大量来回沟通。后面如果还想再往外延可以考虑把这套注释格式做成代码片段模板新建表单文件时自动带出骨架连要不要写注释这个犹豫都省掉。
阅读完成 · 觉得有帮助?