本文将探讨一种前端表单验证模式:当用户在输入框中输入内容时,弹出动态下拉建议列表,用户必须从中选择一项,而不能直接提交手动输入的值。通过使用 blur 事件结合 setTimeout 机制,可以防止事件冲突,并将焦点自动拉回输入框,引导用户进行正确选择。
该需求的核心在于区分用户“想要选择”的内容与“实际输入”的内容。在用户输入时,列表会动态显示过滤后的选项(比如输入“mee”显示“MEETINGS”);点击列表项后,立即将选中文本填入输入框并隐藏列表;当用户离开输入框(blur)时,通过延迟检查当前值是否属于合法选项,若不合法则报错并重新聚焦。
一个常见的难点在于 blur 事件与列表项点击的执行顺序冲突。当点击 li 元素时,会先触发 blur 事件,但此时输入框的值尚未更新(因为点击事件的处理逻辑可能稍后执行),从而导致误判为“未选择”。解决方案很简单:使用 setTimeout(…, 0) 将校验逻辑推迟到下一个事件循环,确保 DOM 更新和值赋值完成后再进行校验。
const input = document.getElementById('myInput');
const list = document.getElementById('myUL');
const items = list.querySelectorAll('li');
// 点击列表项:同步设置输入值并隐藏列表
items.forEach(item => {
item.addEventListener('click', () => {
input.value = item.textContent.trim();
list.style.display = 'none';
});
});
// blur 时延迟校验(避免竞态)
input.addEventListener('blur', () => {
setTimeout(() => {
const currentValue = input.value.trim();
const validOptions = Array.from(items).map(li => li.textContent.trim());
if (!validOptions.includes(currentValue)) {
alert('请从下拉列表中选择有效订单号,不可手动输入!');
input.focus();
input.select();
}
}, 0);
});
需要留意几个细节:
- 避免依赖 onfocus 或 onchange 事件:focus 无法捕获“离开时未选择”的场景;change 仅在值真正改变且失焦后才触发,但无法区分是手动输入还是从列表选择。
- 校验应基于真实的合法选项集合:建议将合法值存储在数组或 data-value 属性中,不要仅依赖 textContent(易受空格、格式等影响)。
- 优化用户体验:可添加视觉反馈(如输入框边框变红)、禁用提交按钮,或在输入框下方显示实时提示(如“请选择:MEETINGS, MAINTENANCE…”)。
- 多字段扩展应用:如果需要对多个字段(如 dateworked、hours)应用相同逻辑,建议封装为可复用函数,传入 inputEl、listEl 和 optionsArray 参数。
该模式在业内常被称为“autocomplete-enforced selection”或“strict suggestion binding”,本质上是将自由输入框升级为受控的“伪下拉组件”,既兼顾搜索效率又保证数据一致性,是企业级表单中保障主数据准确性的常用实践。
