订单页的金额输入框写了 type=number,测试提了个 bug:输入 12e3 提交后变成 12000,输入 1,200 直接成空值。前端还纳闷,明明加了必填属性。

number 类型只适合纯数字、不需要格式化的场景。它允许 e、E、正负号和小数点,内容非法时 value 返回空字符串,validity.badInput 才是 true,Number(value) 拿到的是 NaN。

金额和手机号别用 number

金额建议 type=text 配 inputmode=decimal,提交前自己 parse,固定两位小数。手机号、验证码用 type=text 加 inputmode=numeric,既避免前导 0 被吃掉,也避免移动端弹出带加减号的键盘。验证码再加 autocomplete=one-time-code,iOS 和安卓能直接读短信填充。

中文输入法会把值写脏

用户打拼音时 input 事件一直在触发,值里是 n i h a o 这样的字母串。监听 compositionstart 和 compositionend,组合期间跳过校验,结束再取一次值。搜索框做实时联想时这个问题最明显,不处理就会出现拼音字母被当成关键词提交。

校验交给浏览器,但别只靠它

required、pattern、minlength、maxlength、min、max、step 都能用,样式配合 :invalid 和 :valid。要自定义提示就 setCustomValidity,注意每次输入后要清空,否则会一直红着。form.reportValidity() 能主动弹出提示气泡。前端校验只是体验,服务端必须再验一遍。

几个容易踩的属性

  • placeholder 不是标签:输入后提示就没了,配 label for 才可访问
  • file 的 accept:只是过滤选择框,改后缀照样能传,类型得在后端判
  • autocomplete:登录框写 username 和 current-password,别让浏览器填错位置
  • disabled 与 readonly:前者不提交也不可聚焦,只想锁值就用 readonly

写法大概是这样:<input type='text' inputmode='decimal' pattern='[0-9]+([.][0-9]{1,2})?' required>。pattern 按整串匹配,不用加锚点,也别写反斜杠转义。

输入框的坑多半来自「以为类型能管住数据」。类型只影响键盘和基础校验,真正的边界还是得在提交那一刻算清楚。