step 属性对 type="time" 仅以秒为单位控制提交校验,不能限制 UI 选择;实现半小时粒度需设 step="1800",并配合 JS 校验与初始值对齐,否则用户仍可输入非法时间。

step 属性对 type="time" 输入框**只控制秒级粒度,且仅在部分场景生效**——它不能强制 UI 限制为“只显示 :00 和 :30”,也不能阻止用户手动输入任意分钟值。真正起作用的,是单位换算、初始值对齐、以及 JS 补位校验。
step 的单位是秒,不是分钟
浏览器把 step 当作纯整数秒处理。写 step="30" 是每 30 秒一档(如 09:00:00、09:00:30、09:01:00),不是“每 30 分钟”。要实现半小时粒度,必须换算:30 分钟 = 1800 秒 → step="1800"。
-
step="60"→ 每分钟可选("09:00"、"09:01"…) -
step="1800"→ 每半小时可选("09:00"、"09:30"、"10:00"…) -
step="3600"→ 每小时可选("09:00"、"10:00"…) -
step="15"或step="0.5"→ 浏览器直接忽略该属性
value 必须匹配 step 单位格式
设了 step="1800",但 value="09:15" 会失效——因为 "09:15" 不含秒,而 1800 秒步长要求时间点必须落在整半小时上,且浏览器期望 value 是合法对齐值。
- 正确写法:
value="09:00"、value="09:30"、value="10:00" - 错误写法:
value="9:00"(缺前导零)、value="09:00:00"(带秒但 step 不支持秒级)、value="09:15"(非 30 分钟倍数) - 初始化时若
value不合规,Chrome/Safari 会静默修正为最近合法值,导致 UI 行为异常
浏览器原生控件不尊重 step 的 UI 约束
即使写了 step="1800",Chrome/Firefox/Edge 的时间滚轮仍允许滑动选择所有分钟;iOS Safari ≤15.4 完全不支持 type="time",降级为普通文本框;Android WebView 表现不稳定。也就是说:step 只影响表单提交时的 validity.stepMismatch 校验,不控制交互过程。
立即学习“前端免费学习笔记(深入)”;
- 用户仍可输入
"09:17",点击提交才报错 - 移动端软键盘时间选择器基本无视
step,只按系统默认逻辑渲染 - 想真正锁定可选项?用
<select>动态生成["00:00", "00:30", ..., "23:30"]最可靠
JS 校验和自动对齐是刚需
依赖 step 做精度控制等于裸奔。必须监听 change 或 blur,把非法输入四舍五入或截断到最近 30 分钟点。
const input = document.querySelector('input[type="time"]');
input.addEventListener('change', () => {
if (!input.value) return;
const [h, m] = input.value.split(':').map(Number);
const totalMinutes = h * 60 + m;
const rounded = Math.round(totalMinutes / 30) * 30;
const newH = Math.floor(rounded / 60) % 24;
const newM = rounded % 60;
input.value = `${newH.toString().padStart(2, '0')}:${newM.toString().padStart(2, '0')}`;
});- 用
Math.round()实现四舍五入("09:16"→"09:30") - 用
Math.floor()实现向下取整("09:29"→"09:00") - 空值判断必须加,否则
split报错 - 仅监听
input事件不够——用户可能跳过、粘贴、失焦后提交
真正难的不是写 step="1800",而是意识到它只是个弱约束;实际项目里,UI 控制靠 <select>,精度兜底靠 JS 校验,后端还得再验一次。漏掉任一环,时间就可能歪掉 15 分钟。



















