:placeholder-shown 并非用于“直接实现悬浮占位符”的独立方案——它本质上只是一个状态检测开关。真正驱动“上浮”视觉动作的是 label 元素,而 :placeholder-shown 的核心职责是告知浏览器:“当前 placeholder 正在显示,意味着输入框处于空状态。” 相较于依赖 :focus 或通过 JavaScript 判断 value === "" 的做法,这种机制更可靠、性能更轻量。下面我们逐步拆解其原理。
为什么必须设置 placeholder 属性才能触发 :placeholder-shown
该伪类的匹配逻辑并非“检测输入内容是否为空”,而是“检测浏览器当前是否正在渲染 placeholder 文本”。如果输入框缺少 placeholder 属性(即便设置空字符串 placeholder="" 也符合要求),浏览器将不会启用占位渲染逻辑,此时 :placeholder-shown 始终为 false。以下是一些容易被忽略的细节:
placeholder=" "(空格)与placeholder=""在语法上均合法,但后者更为干净,可避免空格被误识别为实际提示文字。placeholder属性必须直接写在 HTML 结构中。通过 JS 动态设置(如input.setAttribute('placeholder', ''))在部分旧版 Safari 浏览器中可能无法正确触发该伪类。- 移除
placeholder属性后,即使 input 值为空,:placeholder-shown也不会匹配——这是规范定义的行为,而非缺陷。
:placeholder-shown 与 :not(:placeholder-shown) 的分工机制
浮动标签的两个关键状态正是依靠这对伪类进行区分:
input:placeholder-shown + label→ label 保持在初始位置(即下沉状态)。input:not(:placeholder-shown) + label→ label 执行上浮并缩放(适用于已输入内容或失去焦点但非空的情况)。- 需注意选择器的书写顺序:
:not(:placeholder-shown)的规则必须置于:placeholder-shown之后,否则会被后者覆盖。 - 用户输入一个空格、全角空格或换行符时,
:placeholder-shown会立即失效。这是它相比 JS 的trim()方法更底层的优势,但也是一个陷阱:粘贴不可见字符可能导致“看起来为空,但标签未上浮”的情况。
为什么仅靠 :placeholder-shown 无法实现完整的浮动逻辑
该伪类只回答“placeholder 当前是否正在显示”,并不涉及“是否获得焦点”或“是否通过校验”等状态。要实现健壮的浮动标签效果,以下组合缺一不可:
input:focus-within + label→ 处理“输入框为空但已聚焦,label 应上浮”的场景。input:valid + label→ 覆盖required字段校验通过后的固定上浮状态。- 单独使用
:placeholder-shown无法区分“空且聚焦”与“空且未聚焦”,在视觉上会遗漏一次关键的过渡动画。 - IE 浏览器完全不支持该伪类,Edge 16+ 才开始提供支持;Safari ≤15.4 存在偶发的不触发问题,需配合
::-webkit-input-placeholder编写回退样式。
一个真正容易被忽视的要点是:浮动效果依赖于 label 的定位上下文。如果没有为 label 设置 position: relative,那么 transform: translateY() 会以视口为基准进行偏移,导致标签“飘出屏幕”。这并非动画代码写错,而是定位链条断裂所致。
