input[type="time"]的min/max必须是严格HH:MM格式字符串(如"09:00"),"9:00"、"09:0"等均被静默忽略;需用setAttribute设置,服务端必须二次校验。

input[type="time"] 的 min/max 必须是 HH:MM 格式字符串
浏览器只认严格 HH:MM(24 小时制,两位小时 + 两位分钟)格式,比如 "09:00" ✅,"9:00" ❌、"09:0" ❌、"09:00:00" ❌、"09:00 AM" ❌——任何偏差都会被静默忽略,UI 不变灰、输入不拦截、checkValidity() 返回 false 却无提示。
常见翻车点:
-
new Date().toTimeString().slice(0, 5)可能返回"19:20"(正确),但也可能在某些时区或系统下返回带秒或时区的字符串,务必用toISOString()或手动拼接 - 手拼
"${h}:${m}"时没补零,比如h=9, m=5→"9:5",字典序比较会出错("9:5" > "09:00"成立) - 后端传来的
"09:00:00"或"9:00",必须前端清洗:timeStr.slice(0, 5)或正则timeStr.match(/^\d{1,2}:\d{2}/)?.[0]
动态设置 time 的 min/max 必须用 setAttribute 或触发重绘
直接写 input.min = "09:00" 在 Safari(尤其是 iOS ≤ 16.4)和部分旧版 Chrome 中,时间选择器 UI 不刷新,用户仍能点选超限时间。真正生效的方式只有两种:
- 用
input.setAttribute('min', '09:00')—— 触发 DOM 属性变更,强制重绘控件 - 赋值后手动触发更新:
input.dispatchEvent(new Event('input', { bubbles: true })),或input.focus(); setTimeout(() => input.blur(), 10) - 如果当前
input.value已超出新max(如原值是"18:30",新max = "17:00"),浏览器不会自动清空,必须显式判断:if (input.value && input.value > input.max) input.value = ''
联动起止时间时,min/max 清空逻辑不能漏
做“结束时间 ≥ 开始时间”这类联动,只设 end.min = start.value 是不够的。一旦用户清空了开始时间,end.min 会被卡在一个已不存在的值上,导致结束时间永远无法选择任何值。
立即学习“前端免费学习笔记(深入)”;
实操要点:
- 监听
start的change和blur(Safarichange不稳定,blur兜底):若start.value为空,必须执行end.removeAttribute('min')或end.min = '' - 监听
end的change:若end.value && start.value && end.value ,需调用 <code>end.setCustomValidity('结束时间不能早于开始时间')并立即end.reportValidity() - 注意:时间字符串比较是字典序,
"10:00" 为 <code>false,但"9:00" 会因位数不等出错,所以所有值必须统一为 <code>HH:MM格式再比
min/max 不是安全锁,手动输入和粘贴仍可绕过
min 和 max 对 type="time" 仅影响原生时间选择器的可点范围(如滚轮、数字增减按钮),完全不限制键盘输入、粘贴、开发者工具修改等行为。
必须配合 JS 实时校验:
- 监听
input事件,对输入值做格式 + 范围双重检查:if (!/^\d{2}:\d{2}$/.test(val) || val input.max) - 非法时建议清空:
input.value = '',或设自定义错误:input.setCustomValidity('时间超出允许范围') - 服务端必须二次解析并校验——HTTP 请求里传什么,后端就收到什么;攻击者可直接 POST
start_time=23:59,哪怕前端设了max="18:00"
最易被忽略的是:不同浏览器对 type="time" 的实现差异极大——iOS Safari 默认弹出滚轮但不显示增减按钮,Android 部分厂商浏览器甚至不支持 min/max UI 约束,只在提交时校验。别假设用户只会点控件。



















