:focus等伪类需配合属性与js事件,ios/旧安卓webview中仅写伪类易失效;:invalid/:valid非实时触发,:user-invalid更可靠;禁用:focus-within控制子元素;须显式重置outline/appearance并兼顾可访问性。

input 标签本身不产生任何状态样式,所有视觉变化都依赖属性 + CSS 伪类 + JS 事件三者配合;光写 :focus 或加 disabled 属性,不处理浏览器默认行为和兼容性细节,大概率在 iOS 或旧版安卓 WebView 中失效。
如何用 CSS 伪类控制 focus/invalid/valid 等状态样式
浏览器原生只提供有限的伪类支持,且触发时机有隐含规则:
-
:focus在用户主动点击或 Tab 进入时生效,但 iOS Safari 对input[type="date"]或软键盘弹出延迟的场景常不触发 -
:invalid和:valid不是实时计算的——空值时required字段默认为:invalid,但用户未输入前,部分浏览器(如 Chrome 120+)可能不渲染对应样式 -
:user-invalid更可靠,它只在用户编辑后验证失败才激活,适合做“已操作但错”的反馈 - 别用
:focus-within套父容器来控制子 input 的边框色——安卓 WebView 69–75 版本中该伪类完全不响应
推荐写法示例(兼顾可读与兼容):
input {
border: 1px solid #ccc;
}
input:focus {
outline: none;
border-color: #007BFF;
}
input:invalid:not(:placeholder-shown) {
border-color: #FF0000;
}
input:valid:not(:placeholder-shown) {
border-color: #28A745;
}
disabled 和 readonly 状态下样式的差异与陷阱
两者语义不同,浏览器渲染和交互逻辑也完全不同,不能混用或随意切换:
-
disabled:元素脱离表单流,:disabled伪类匹配,CSS 中可设opacity: 0.6或background-color: #f5f5f5,但注意它不触发focus、input事件,JS 中el.value仍可读,但提交时不会被序列化 -
readonly:仍参与表单提交,:read-only可匹配,但 iOS Safari 软键盘可能意外弹出,且无法阻止用户长按复制——若需禁复制,得额外加oncopy="return false" - 同时设置
disabled和readonly时,disabled优先级更高,readonly被忽略 - 动态切换建议用 JS 属性赋值:
el.disabled = true或el.setAttribute('readonly', '');避免el.readOnly = true(大小写敏感,IE 下无效)
如何稳定覆盖浏览器默认边框与焦点轮廓
各浏览器对 input 的默认边框、阴影、焦点环(outline)实现不一致,尤其在移动端:
- Chrome 和 Edge 默认有
outline: -webkit-focus-ring-color auto 1px,必须显式写outline: none才能清除 - Safari 会保留系统级焦点环,仅靠
outline: none不够,需加-webkit-appearance: none配合重绘边框 - Firefox 在高对比度模式下会强制显示 outline,此时应改用
outline: 2px solid transparent替代none,再通过box-shadow模拟聚焦效果 - 务必加
box-sizing: border-box,否则padding和border会让实际宽度超出设定值
最小可用重置组合:
input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
box-sizing: border-box;
outline: none;
border: 1px solid #ccc;
padding: 8px 12px;
}
input:focus {
border-color: #007BFF;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.2);
}
为什么 pattern 和 required 验证状态总“不生效”
HTML5 表单验证不是监听输入流的 reactive 系统,它的状态更新有明确前提:
-
pattern正则默认是全匹配(即隐式包裹^...$),写pattern="\d+"无法匹配"123abc",但用户也不会看到提示——除非调用reportValidity() -
checkValidity()只返回布尔值,不触发 UI 反馈;要让错误样式立刻出现,必须手动调用reportValidity() - 自定义错误信息要用
setCustomValidity("xxx"),并在input或blur事件里清空(setCustomValidity("")),否则后续验证永远失败 - 不要在
invalid事件里直接修改value——这会打断原生验证流程,导致:invalid状态卡死
关键点:验证状态不是“写完就有效”,而是“用户交互动过 + 主动触发反馈”才可见。很多团队卡在这一步,不是代码写错了,是没意识到浏览器验证是 lazy 的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











