step属性不拦截非法字符,仅用于数值校验;onkeypress+charCode易失效,因它无法识别软键盘、粘贴、IME输入及部分控制键(如Backspace),且现代浏览器中charCode已废弃,推荐改用keydown+key或input+正则替换。

step 属性本身**完全不处理非法字符输入**——它只参与数值合法性校验(比如 value 是否满足 min + n × step),对键盘敲入的 .、e、-、多个小数点、字母等,浏览器一律放行。想拦住这些字符,必须用 JavaScript 主动干预。
为什么 onkeypress + charCode 拦截容易失效
很多教程推荐用 onkeypress="return event.charCode >= 48 && event.charCode 只放行数字键,但它有硬伤:
- 无法拦截粘贴(
paste事件)、拖拽、右键粘贴、IME 组合输入(如中文输入法下按1出来的是「一」) -
charCode在现代浏览器中已废弃,key或code更可靠但逻辑更复杂 - 禁掉
-后,负数输入直接被砍断;禁掉.后,小数输入无解
真正可用的 input 事件过滤方案(保留光标位置)
必须在 input 事件里做字符串清洗,并手动恢复光标,否则光标会跳到末尾。关键不是“删什么”,而是“删完怎么不丢位置”:
- 先记录
selectionStart和selectionEnd - 用正则提取合法部分(例如
/^-?\d*\.?\d*$/允许负号、小数点、数字,但禁止..或-.3) - 计算被删字符数:原字符串长度 − 清洗后字符串长度
- 若光标在被删区域之后,新光标位置 = 原位置 − 被删数;若在中间或之前,需按实际删除位置偏移
- 赋值后调用
input.setSelectionRange(newStart, newEnd)
简例(仅允许一位小数):
input.addEventListener('input', e => {
const { value } = e.target;
const match = value.match(/^(-?\d*\.?\d{0,1})/);
const cleaned = match ? match[1] : '';
const start = e.target.selectionStart;
const end = e.target.selectionEnd;
<p>if (cleaned !== value) {
e.target.value = cleaned;
const delta = value.length - cleaned.length;
const newStart = start > delta ? start - delta : 0;
const newEnd = end > delta ? end - delta : 0;
e.target.setSelectionRange(newStart, newEnd);
}
});
type="number" 的底层限制你绕不开
即使 JS 过滤了所有非法字符,type="number" 仍有隐藏行为:
立即学习“前端免费学习笔记(深入)”;
- 用户粘贴
"12.34.56",value可能变成"12.34"(浏览器自动截断),但valueAsNumber是NaN - 输入
"1e2",value是字符串"1e2",valueAsNumber是100,但业务可能不允许科学计数法 - 旧版 iOS Safari 会把
step="any"当作无效,仍按step="1"校验,导致小数被清空
所以,**不要依赖 type="number" 做字符控制**。该用 type="text" + inputmode="decimal" + JS 校验的场景,就果断换掉。
最易被忽略的一点:step 和字符过滤是两层事——前者管“数值是否落在合法网格上”,后者管“字符串是否符合业务格式”。混用或只做其一,都会在移动端、粘贴、IME 场景下崩掉。



















