:not(:placeholder-shown) 单独失效因它仅在placeholder消失瞬间触发,无法响应空输入聚焦、全角空格、Tab进入已填字段及label非紧邻等场景,须联合:focus与:valid(需required)或JS补全状态。

:placeholder-shown 本身不能制作浮动标签——它只是个“占位符是否正在显示”的快照信号,不感知焦点、不校验内容、也不维持状态。单靠它写 input:not(:placeholder-shown) + label,标签会在删空后立刻掉回、在已填字段失焦时瞬间塌陷,交互断裂。
为什么 :not(:placeholder-shown) 单独用会失效
这个选择器只在 placeholder 消失的瞬间生效,但以下情况它完全不响应:
- 用户点入输入框但还没输任何内容 →
:placeholder-shown仍为 true,:not()不触发,标签不动 - 输入一个全角空格、换行符或零宽字符 → 值逻辑为空,但
:placeholder-shown已失效,标签提前上浮又无后续维持 - 光标点进已有值的字段(比如从别处 Tab 进来)→
:placeholder-shown早就不匹配,:not()也未定义聚焦态,标签直接掉落 - DOM 中
label不紧邻input(中间有注释、span、图标)→+选择器断开,规则彻底不生效
必须组合 :focus 和 :valid(或 JS)补全状态
纯 CSS 可行路径只有一条:加 required 属性,用 :valid 捕获“有值”状态。三者协同覆盖全部场景:
-
input:placeholder-shown + label:初始空态,标签在默认位置 -
input:focus + label:聚焦即上浮,解决“空但已点入”问题 -
input:valid + label:有值即上浮,解决“非空失焦”问题(依赖required)
三者共用同一段 transition: transform 0.2s ease,避免瞬移;label 必须设 position: relative,否则 transform: translateY() 基准错乱。
立即学习“前端免费学习笔记(深入)”;
结构与属性漏一项,整个链路就断
以下硬性条件缺一不可:
-
label必须紧跟input后面,中间不能有换行、注释或任何元素(+是紧邻兄弟选择器) -
input必须带placeholder属性,哪怕写成placeholder=" "(一个空格),空字符串placeholder=""在旧 Safari 中不触发 -
input要预留上浮空间,例如padding-top: 1.5rem,否则文字被遮挡 -
label需加pointer-events: none,否则会拦截input的点击
不加 required 就只能靠 JavaScript:监听 input 事件加 has-value 类,监听 blur 事件按值是否为空决定保留或移除该类,再用 .has-value + label 替代 :valid。
真正难的不是写出那几行 CSS,而是理解 :placeholder-shown 从不“记住”状态——它每次重绘都重新判断 placeholder 是否正显示,而用户交互是连续的,浏览器渲染是离散的。


















