:placeholder-shown是唯一靠谱的“输入框是否为空”状态判断依据,因它由浏览器渲染层直接反馈——仅当placeholder文本实际显示时匹配,用户输入任意可见字符(含空格、全角空格等)即失效;它要求input必须有placeholder属性(如placeholder=""),不适用于type="hidden"、disabled或contenteditable元素。

:placeholder-shown 伪类不是用来“动画 placeholder 本身”的,而是浏览器原生提供的、最精准的“输入框是否为空”状态信号。它比 input.value === "" 可靠得多,也比单纯监听 :focus 更符合浮动标签的真实需求。
为什么 :placeholder-shown 是唯一靠谱的状态判断依据
用户输入一个空格、全角空格、换行符,甚至粘贴一段带首尾空白的文本,input.value.trim() === "" 都可能误判;而 :placeholder-shown 是浏览器渲染层直接反馈的状态——只要 placeholder 文本正在显示,它就匹配;一旦任何可见字符(或非空白 Unicode 字符)进入输入框,它立刻失效。
- 它不适用于
type="hidden"、disabled或contenteditable元素 - 它要求
input必须有placeholder属性(哪怕值是placeholder=""或placeholder=" "),否则永远不触发 - 在 iOS Safari 15.4 之前,部分输入法弹出时会短暂丢失该状态,需注意兼容性兜底
:not(:placeholder-shown) 才是浮动标签真正要监听的核心状态
浮动标签的关键不是“用户点进来了”,而是“用户已经填了东西”。只靠 :focus 会导致失焦后标签掉回框里,盖住已输入内容;:not(:placeholder-shown) 则天然覆盖“已输入”这一语义,且持续生效直到内容被清空。
- 必须配合兄弟选择器使用:
input:not(:placeholder-shown) + label(相邻)或input:not(:placeholder-shown) ~ label(后续任意兄弟) - 若
label不紧跟input,又不想用兼容性差的:has(),就得把两者包进同一父容器,并给父容器设position: relative - 不要漏掉
required场景:可叠加input:valid + label强化校验通过态,避免数字框输0被误判为空
label 上浮必须满足三个硬性前提
写 transform: translateY(-1.5em) 却没效果?大概率卡在这三件事上:
-
label或其父容器没设position: relative——transform没有定位上下文,偏移基准是视口左上角,文字直接“飘走” - 没预留足够
padding-top给上浮后的label留空间,结果文字被输入内容顶穿 - 用了
top替代transform—— 触发重排,iOS Safari 动画明显卡顿,且影响可访问性
推荐过渡只写 transition: transform 0.2s ease, opacity 0.2s ease,避免 transition: all 拖慢渲染。
placeholder 属性怎么设才不踩坑
很多人以为 placeholder="" 无效,其实它是合法且推荐的写法;placeholder=" "(空格)也能用,但容易被误读为真实占位文本。
- 动态填充内容后,CSS 状态不会自动更新 —— 必须手动触发
input.dispatchEvent(new Event('input', { bubbles: true })) - Bootstrap 5 的
form-floating类强制要求placeholder存在,且label必须紧随input后,顺序错一位整个浮动就失效 -
select元素不支持:placeholder-shown,它的“浮动”只是静态定位模拟,别指望动画
最隐蔽的坑是:label 和 input 的 for/id 不匹配,不仅屏幕阅读器无法关联,某些浏览器下 :focus-within 也会失效,间接导致组合状态漏判。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











