纯 css 浮动标签需用 :focus-within 包裹父容器并设 placeholder=" ",配合 transform 动画与 will-change 优化;无 required 时必须 js 监听 input/blur 动态加 has-value 类兜底。

:placeholder-shown 本身不能“优化”浮动标签,它只是个脆弱的触发开关;真要稳定上浮,必须用 :focus-within 或 :focus + :valid 组合,且 placeholder 属性不能丢——哪怕只设一个空格。
为什么单独写 input:not(:placeholder-shown) + label 会掉帧
这个选择器只在 placeholder 消失时生效,但以下情况都会让它断开:
- 用户聚焦输入框但还没输内容 →
:placeholder-shown仍为 true,:not()不触发,标签不动 - 输入一个全角空格、零宽字符或换行符 → 值逻辑为空,但
:placeholder-shown已失效,标签瞬间掉落 - DOM 中
label不紧邻input(中间有注释、span、图标或换行)→+选择器完全不匹配
必须用 :focus-within 包裹父容器才可靠
它不依赖 placeholder 是否显示,也不挑 input 类型,是目前最健壮的纯 CSS 方案:
- HTML 结构必须是:
<div class="field"> <input placeholder=" "><label>邮箱</label> </div> - CSS 规则顺序很重要:
.field:focus-within label要写在input:not(:placeholder-shown) + label后面,否则会被覆盖 - 旧版 Safari(≤15.3)不支持
:focus-within→ 必须加@supports not selector(:focus-within)回退到input:focus + label -
placeholder=" "(一个空格)不能省略,空字符串placeholder=""或不写该属性,:placeholder-shown永远不匹配
transition 和 transform 的坑比你想的多
90% 的“标签动不起来”问题出在动画配置上:
-
transition必须写在label的默认样式里,不能只写在激活态规则中,否则首次上浮无动画 - 别用
top+transition: top→ 会触发 layout,iOS 滚动卡顿;改用transform: translateY(-24px) scale(0.85) - 务必加
will-change: transform,尤其 iOS Safari,否则动画掉帧明显 -
input要预留足够padding-top(比如1.5rem),否则文字被上浮的label遮住 - 加
input:focus::placeholder { color: transparent }防止原生占位符和 label 重叠;Safari 不稳时改用opacity: 0
不加 required 就只能靠 JavaScript 补位
业务字段可选、不能加 required 时,CSS 没有兜底方案:
-
:valid依赖required,没它就失效;:not(:placeholder-shown)在失焦后也失效 - 必须监听
input和blur事件,动态切换has-value类 - 注意边界:粘贴、剪切、清空按钮、键盘快捷键(如 Cmd+A → Delete)都要覆盖,建议用
requestAnimationFrame批量读值 - CSS 改用
.has-value + label替代:valid,同时保留input:focus + label处理空聚焦态
真正难的不是写对那几行 CSS,而是 placeholder 被 JS 清空、value 被预设、第三方库劫持 focus、Safari 早期版本解析错乱——这些状态组合起来,让纯 CSS 方案在真实项目中几乎必然需要 JS 降级兜底。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











