如何阻止用户在Web应用日期时间输入框中输入无效日期
实时验证MM/DD/YYYY HH:MM:SS格式的日期时间输入框
需求:开发Web应用的日期时间输入框,要求输入格式为MM/DD/YYYY HH:MM:SS,需要实时阻止无效输入(比如月份输入13、日期输入32等),而非输入后再验证。现有代码已实现自动格式化,但缺少实时验证逻辑,尝试用keypress事件的prevent.default()未成功。
修改后的代码
HTML代码
<label>Date time: <input placeholder="__/__/____ __:__:__" data-slots="_"> </label><br>
JavaScript代码
document.addEventListener('DOMContentLoaded', () => { for (const el of document.querySelectorAll("[placeholder][data-slots]")) { const pattern = el.getAttribute("placeholder"), slots = new Set(el.dataset.slots || "_"), prev = (j => Array.from(pattern, (c, i) => slots.has(c) ? j = i + 1 : j))(0), first = [...pattern].findIndex(c => slots.has(c)), accept = new RegExp(el.dataset.accept || "\d", "g"), // 定义各字段的位置范围和验证规则 fieldRules = [ { start: 0, end: 2, max: 12, min: 1 }, // 月份 MM { start: 3, end: 5, max: (year) => { // 日期 DD,根据年份和月份判断最大天数 const month = parseInt(el.value.slice(0,2),10) || 1; const isLeap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); const monthDays = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; return monthDays[month - 1]; }, min: 1 }, { start: 6, end: 10, max: 9999, min: 1900 }, // 年份 YYYY { start: 11, end: 13, max: 23, min: 0 }, // 小时 HH { start: 14, end: 16, max: 59, min: 0 }, // 分钟 MM { start: 17, end: 19, max: 59, min: 0 } // 秒数 SS ], clean = (input) => { input = input.match(accept) || []; return Array.from(pattern, c => input[0] === c || slots.has(c) ? input.shift() || c : c ); }, format = () => { const [i, j] = [el.selectionStart, el.selectionEnd].map(i => { i = clean(el.value.slice(0, i)).findIndex(c => slots.has(c)); return i < 0 ? prev[prev.length - 1] : back ? prev[i - 1] || first : i; }); el.value = clean(el.value).join``; el.setSelectionRange(i, j); back = false; }, // 验证当前输入是否合法 isValidInput = (key, pos) => { if (!/\d/.test(key)) return false; // 非数字直接不允许 // 找到当前光标位置对应的字段 const field = fieldRules.find(f => pos >= f.start && pos < f.end); if (!field) return true; // 不在字段范围内,允许输入(比如分隔符位置) // 模拟输入后的字段值 let currentValue = el.value.split(''); // 替换当前光标位置的字符 currentValue[pos] = key; const fieldStr = currentValue.slice(field.start, field.end).join('').replace(/_/g, ''); const fieldNum = parseInt(fieldStr, 10); // 如果是日期字段,需要先获取年份 if (field.start === 3) { const yearStr = currentValue.slice(6,10).join('').replace(/_/g, ''); const year = yearStr ? parseInt(yearStr,10) : new Date().getFullYear(); const maxDay = typeof field.max === 'function' ? field.max(year) : field.max; return fieldNum >= field.min && fieldNum <= maxDay; } // 其他字段直接验证范围 return fieldNum >= field.min && fieldNum <= field.max; }; let back = false; el.addEventListener("keydown", (e) => { back = e.key === "Backspace"; // 允许特殊按键:退格、删除、方向键、Tab、回车等 const allowedKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Enter']; if (allowedKeys.includes(e.key)) return; // 阻止非数字输入和无效值输入 if (!isValidInput(e.key, e.target.selectionStart)) { e.preventDefault(); } }); el.addEventListener("input", format); el.addEventListener("focus", format); el.addEventListener("blur", () => el.value === pattern && (el.value = "")); } });
关键实现说明
- 字段规则定义:通过
fieldRules数组定义每个日期时间字段的位置范围、最小/最大值,其中日期字段的最大值会根据年份和月份动态计算(处理闰年2月的情况)。 - 实时验证逻辑:在
keydown事件中,先判断是否为允许的特殊按键,然后调用isValidInput函数验证输入的数字在当前字段是否合法,不合法则调用e.preventDefault()阻止输入。 - 动态日期验证:日期字段验证时会优先读取已输入的年份,若年份未输入则默认使用当前年份,确保日期范围的准确性。
内容的提问来源于stack exchange,提问作者J GURURAJ
相关产品推荐
相关产品推荐

