input事件需手动调用checkValidity()才能触发UI反馈,预置error-feedback节点并用visibility隐藏,setCustomValidity前必须清空否则校验卡死。

input 事件是实时校验的触发基准,但仅绑定它不等于有可用反馈——必须同步更新 DOM 状态、语义属性和视觉样式,否则用户看不到、读屏器读不到、自动化测试也抓不住。
为什么 input 事件触发后没反应?
浏览器不会因为值变了就自动标红或弹提示。原生校验(required、type="email")只在 submit 或显式调用 checkValidity() 时才激活 UI 反馈。
- 必须手动调用
element.checkValidity(),才能让:user-invalid伪类生效、边框变色、validationMessage可读 - 只改
value不调checkValidity()→ 样式不动、aria-invalid不变、JS 判断仍是true - 别依赖
invalid事件监听——它不冒泡到单个 input,且只在提交或reportValidity()后触发,无法用于实时
怎么让错误提示不闪跳又保持可访问?
错误信息必须是预置的 DOM 节点,且始终存在文档流中;靠 display: none 控制显隐会引发布局抖动,也破坏屏幕阅读器焦点流。
- 每个
<input>后紧跟一个<div class="error-feedback" role="alert" aria-live="polite"></div> - CSS 用
visibility: hidden; height: 0; overflow: hidden;隐藏,而非display: none - JS 中用
feedbackEl.textContent = errorText更新文案,再用feedbackEl.style.visibility = errorText ? 'visible' : 'hidden' - 同时设置
input.setAttribute('aria-invalid', !!errorText)和input.setAttribute('aria-describedby', errorText ? feedbackEl.id : null)
setCustomValidity() 怎么用才不卡死校验?
这个 API 不是“设了就生效”,而是状态标记器:设非空字符串即锁定为无效,直到你主动清空。漏掉清空步骤,用户输对了也过不了。
立即学习“前端免费学习笔记(深入)”;
- 每次校验前必须先执行
input.setCustomValidity(''),否则后续checkValidity()永远返回false - 校验失败时再调
input.setCustomValidity('邮箱格式不对')(注意不加句号,浏览器会自动补) - 不要在
input事件里反复设同一错误文案——会导致validationMessage始终不变,即使用户已改对 - 若需兼容老 Safari(:user-invalid 不可用,得退回到手动切换
class+aria-invalid
移动端中文输入法下怎么避免误判?
拼音未上屏时 input 事件确实会触发,但现代浏览器(Chrome 90+、Safari 15.4+、Firefox 88+)已统一行为:input 只在“上屏完成”后触发,无需额外防抖处理候选词阶段。
- 唯一需要防抖的场景是异步校验(如用户名查重)——用户连打时避免发一堆请求
- 防抖阈值建议 200–300ms:太短压不住高频输入,太长会让反馈滞后
- 防抖函数必须包裹整个校验逻辑(含
checkValidity()、setCustomValidity()、DOM 更新),不是只包 fetch - 别用
compositionstart/compositionend做开关——当前主流浏览器已不需要,反而增加兼容负担
真正容易被忽略的是语义同步:光让边框变红不够,aria-invalid、aria-describedby、role="alert"、textContent 这四者必须严格一致,缺一不可。否则在 VoiceOver 或 TalkBack 下,用户可能听到“输入框无效”,却找不到错误在哪一行。


















