:placeholder-shown仅在placeholder文本实际可见时匹配,本质是视觉状态伪类而非内容校验工具;输入任意字符(含空格、换行符)即失效,需用:not(:placeholder-shown)响应“有内容”状态。

:placeholder-shown 本来就不匹配“已有内容”的输入框
:placeholder-shown 的设计目的就是**只在 placeholder 文本实际可见时才匹配**,而不是“判断是否有内容”。它本质上是一个视觉状态伪类,不是逻辑校验工具。一旦用户输入哪怕一个字符(或空格、换行符),浏览器就会隐藏 placeholder,该伪类立刻失效——所以你用它“匹配已有内容”本身就是方向反了。
想响应“有内容”状态,正确做法是用 :not(:placeholder-shown),而不是试图让 :placeholder-shown 匹配非空情况。
常见误判场景和对应原因
以下情况都会导致你以为“应该匹配却没匹配”,其实是伪类按规范工作正常,只是你理解错了触发条件:
- 写了
input:placeholder-shown { ... }却期望它在输入后仍生效 —— 不可能,输入后 placeholder 隐藏,伪类自动移除 - HTML 中漏了
placeholder属性,比如只写<input>没加placeholder="..."—— 浏览器根本不会渲染占位符,:placeholder-shown永远不触发 -
input.value = " "(一个空格)或"\n"—— DOM 值非空,placeholder 不显示,伪类不激活 - 用了
type="number"且初始值为"0"或0—— 占位符常被忽略,即使 value 是空字符串,也不显示 placeholder - 在旧版 Safari(≤15.3)中测试 —— WebKit 实现有缺陷:可能聚焦后仍保留伪类,或输入后不及时退出,行为不可靠
为什么不能用 [value*="xxx"] 替代 :placeholder-shown 判断内容
[value*="xxx"] 匹配的是 HTML 属性值(attribute),不是 DOM 的实时 value。用户输入时,input.value 改变了,但 input.getAttribute('value') 仍是初始值(或 null)。所以这个选择器对用户输入完全无感。
真正需要响应输入内容时,必须:
- 监听
input事件,用 JS 判断input.value.trim() === "" - 动态添加/移除 class(如
has-value),再用 CSS 写input.has-value { ... } - 避免依赖
value属性选择器做交互控制
浮动标签、清除按钮这类功能的实际落地要点
纯 CSS 实现浮动标签或清除按钮,核心就两条:
- 结构上确保
label或.clear-btn是input的紧邻兄弟(用+)或通用兄弟(用~),否则选择器不生效 - CSS 规则必须显式写出
input:not(:placeholder-shown) + .clear-btn或input:not(:placeholder-shown) ~ .label,不能只写:placeholder-shown - 移动端要特别注意
type="search"或type="text"的一致性;type="number"在 iOS 上 placeholder 渲染极不稳定,建议统一用type="text"+ JS 校验
:placeholder-shown 表现出“有时生效、有时卡住、有时彻底失灵”的随机行为,这时候 JS fallback 不是可选项,而是必选项。


















