先记住这个答案
step 让输入值必须等于某个步进基数加 n 倍步长,这里的 n 是整数。基数取自 min 属性的值;若没有 min,则取自 value 属性在标记中给出的默认值;两者都没有时基数为 0。默认 step 为 1,所以未声明 step 时只有满足 "与基数相差整数" 的数才有效,例如 min="1.5" 时 2.5 有效而 2.6 无效。step="any" 会关闭这种步进倍数的限制,允许任意小数,但仍然受 min 与 max 范围以及 required 空值规则的约束。
- step 基值顺序为 min、value、0
- step=any 取消步进间隔限制
- 默认 step=1 可能伴随小数基值
步进校验的数学机制与基值推导
step 的校验定义是:若控件的当前数值 t,步长 s,基值 b,则只有当 t 可以写成 b + n·s 的形式且 n 为整数时才通过 stepMismatch 校验。基值 b 的解析优先检查 min 属性是否存在且不空,取其数值;否则检查 value 属性在 HTML 中设定的初始值;若两者都没有,则 b 为 0。举例:<input type="number" min="3" step="2"> 中 b=3,可接受 3、5、7、9,而 4 会被标记为 stepMismatch。
当 step 省略时默认 s=1,这并不等于只能输入整数——如果 b 含小数,例如 min="0.5",则 1.5、2.5 都满足 0.5 + n·1,因为 n=1 时得到 1.5。这种基值偏移常被误读为“step=1 只允许整数”,正确表述是“所输入值必须与基值的差为步长的整数倍”。由于多数输入未设 min,基值回落到 value 或 0,此时整数限制才成立。
一个小数步长导致提交失败的现场
假设某个表单需要采集体重增量,规定范围 50.0 到 100.0 千克,并允许 0.1 的间隔,那么可以写成 min="50" max="100" step="0.1"。当用户输入 50.3 时,浏览器通常认为有效,因为 50.3 与 50 的差是 0.3,可视为 0.1 的 3 倍。但输入 60.2 时,60.2 与 50 的差是 10.2,在二进制浮点中 10.2 / 0.1 约等于 101.99999,因此部分浏览器可能判定它不满足步进倍数,从而给出校验失败。这并非数学错误,而是 IEEE 754 浮点表示的误差。
工程做法是如果数据只需要区间和任意小数,应直接使用 step="any",然后在服务器端用十进制字符串进行校验。若必须保留 0.1 的递增语义,可将 min 改为 0、step 设为 0.1,这样 50.3 对应 503 个步长、60.2 对应 602 个步长,但误差仍可能因 0.1 的二进制近似而存在。更稳妥的方式是改用整数步长(例如用 1 代表 0.1)进行整数运算,或在受控场景下使用 JS 读取 validity.stepMismatch 并加以修正——但这种方法会绕过浏览器原生校验,只适用于明确误差已知的情况。
step 约束的失效条件与浏览器差异
step 属性只接受正数;设为 0 或负数会被视为无效并恢复为默认值 1。step="any" 必须使用 any 字符串(通常不区分大小写,但不能写成数字或其它字符串)。即使 step="any" 也仍然要求控件内必须是合法数字,且若存在 min 或 max,取值必须落在数值区间内;required 为真时空值依旧触发 valueMissing。step 只影响校验,不限制用户手动输入的字符,用户可在编辑过程中输入任意内容,仅在提交时进行有效性判断。
不同浏览器对浮点误差的处理可能不同,某些值(如 60.2 对 0.1)可能被部分浏览器误判为不满足步进。因此不要依赖浏览器原生校验消息作为最终判断,而应在服务端用十进制字符串库重新校验。若需要前端即时提示,可以在 oninput 事件中读取 input.validity.stepMismatch 做交互,但该属性只说明“不匹配”,并不提供最接近的合法值,也不能替代用户输入的原始意图。
容易答错的地方
- 认为 step=1 必然限制为整数
- 当 min 带有小数时基值不再是 0,例如 min="0.5" 搭配默认 step=1,输入 1.5 是合法的,因为 1.5 = 0.5 + 1。所以 step=1 真正限制的是“与基值的差必须为整数”,而不是值本身必须为整数。
- 把 step=any 理解为接受任意文本
- any 只关闭步进间隔校验,number 类型本身仍通过正则排除非数字字符;同时 min、max、required 依然生效。输入 "abc" 会因不是合法数字而报 badInput,并不会因为 step=any 而被放行。
面试官还会怎么问?
step 值可以设置为 0.01 以下吗?
属性语法允许任意大于 0 的浮点数,但步长越小,浮点误差出现的概率越高(例如 0.1 本身在二进制中就是近似值)。通常不建议设到 1e-6 以下,因为步长过小可能使校验产生不确定的误判。
如何用脚本检测当前值是否违反 step 约束?
通过 input.validity.stepMismatch 返回布尔值即可。该属性不需要手动执行浮点减法,浏览器已按内部算法预处理。若 stepMismatch 为 true,说明当前值不满足基数加步长整数倍。
number 输入支持 pattern 属性做额外格式校验吗?
不支持。number 类型的用户界面已经限制可输入字符,因此规范规定 number 输入必须忽略 pattern 属性。需要模式匹配时可改用 inputmode="numeric" 的 text 类型并配合 pattern。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。