必须用 mutationobserver 监听 step 属性变更,配置 attributes: true、attributefilter: ['step']、attributeoldvalue: true,并严格校验 target 和 oldvalue;篡改后需用 setattribute 修改并同步规整 value。

监听 step 属性变更必须用 MutationObserver
直接读取 input.step 或轮询 getAttribute('step') 都不可靠——属性可能被脚本瞬间改写又恢复,人眼和定时器都抓不住。唯一能实时捕获“被篡改”动作的,是 MutationObserver,且必须精准配置,否则会被广告 SDK、埋点脚本等正常操作淹没。
- 只观察目标元素本身,比如
const target = document.querySelector('input[type="number"]');,不要监听document.body - 配置中必须设
attributes: true、attributeFilter: ['step']、attributeOldValue: true - 回调里要严格比对:
mutation.target === target且mutation.attributeName === 'step'且mutation.oldValue !== mutation.target.getAttribute('step')
step 被篡改后浏览器行为会异常
篡改 step 不会立刻报错,但会破坏浏览器内置逻辑:步进器箭头点击失效、方向键微调跳变、checkValidity() 返回意外结果,甚至导致 value 被静默修正(比如从 1.05 变成 1.0)。这些不是 bug,而是浏览器发现当前 value 不再满足新 step 规则后的强制对齐。
- 初始
value="1.0"+step="0.1"合法;若脚本改为step="0.25",1.0仍合法(因为(1.0 - min) / 0.25 === 整数) - 但若改为
step="0.3",而min="0",则1.0不合法((1.0 - 0) / 0.3 ≈ 3.333...),浏览器可能在下次交互时自动跳到0.9或1.2 - 用
input.stepUp(1)测试:如果点击后没反应或跳得不对,大概率是step已被篡改且value未同步规整
动态改 step 后必须重置 value
仅改 step 属性不触发浏览器重新校验 value 合法性。旧值可能越界,导致后续所有交互失常。修复不是“还原原 step”,而是主动规整当前值。
- 别用
input.step = '0.25'—— 这不会触发 DOM attribute 变更,MutationObserver捕捉不到 - 必须用
input.setAttribute('step', '0.25'),再立刻执行:const newStep = 0.25; const min = parseFloat(input.min) || 0; const currentValue = parseFloat(input.value) || min; const alignedValue = min + Math.round((currentValue - min) / newStep) * newStep; input.value = alignedValue.toFixed(Math.max(0, (newStep.toString().split('.')[1] || '').length)); - 最后调用
input.checkValidity()确认状态,必要时input.reportValidity()
验证是否真被篡改,不能只看字符串值
step="0.1" 和 step="0.10" 在 DOM 上看起来一样,但部分浏览器对末尾零敏感;step="any" 和没写 step 语义完全不同(前者跳过 stepMismatch 校验,后者用默认 step=1)。所以比对不能只看 getAttribute('step') 是否变化,还得结合行为验证。
- 用
input.validity.stepMismatch判断当前value是否匹配当前step规则 - 手动输入一个明显非法值(如
min="0" step="0.25"时输0.1),再提交,看是否触发stepMismatch—— 若没触发,说明step很可能被设成了"any"或被移除 - 注意:iOS Safari 对
step="any"支持稳定,但某些 Android WebView 会忽略它,此时行为差异本身就是篡改线索
真实篡改往往藏在修复逻辑里:你以为只是“重置了 step”,其实脚本悄悄把 value 也替换了,或者用 setAttribute 改了 step 却忘了同步更新 min/max,导致整个合法值集合崩塌。盯住属性只是第一步,关键得验证它是否还在起作用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











