浮动标签必须依赖:not(:placeholder-shown)判断输入状态,而非仅:focus;需确保placeholder非空、结构严格、事件同步派发,并统一transition细节以保障状态一致与动画自然。

浮动标签(floating label)不是靠 float 实现的,也不是简单把 label 用 position: absolute 往上拽——它必须同时响应「用户已输入」和「当前聚焦」两种状态,否则一失焦标签就掉下来盖住内容。
:not(:placeholder-shown) 是唯一可靠的状态判断依据
现代浏览器原生支持的 :placeholder-shown 伪类,只在 input 或 textarea 正显示 placeholder 文本时匹配;只要值非空(哪怕只输一个空格),该伪类就失效,:not(:placeholder-shown) 就成立,标签才能保持上浮。
-
placeholder属性必须存在且不能是空字符串(placeholder=""无效),至少写placeholder=" "或placeholder="请输入" - 仅监听
:focus不够:用户填完内容点到别处,label立刻回落,和输入值重叠 -
select元素不触发:placeholder-shown,所以 Bootstrap 的form-floating要求<option disabled selected>请选择</option>才能模拟该行为 - IE 完全不支持该伪类,如需兼容,必须用 JS 监听
input和blur事件手动切换 class
Bootstrap 5 的 form-floating 有硬性结构约束
看似开箱即用,但顺序、属性、选项写法错一点,label 就卡在顶部不动或错位。
-
<label></label>必须紧跟在<input>或<select></select>后面,中间不能插其他元素(包括注释) -
<input>必须带非空placeholder,否则初始渲染时label不会下沉 -
<select></select>的占位选项必须同时含disabled和selected,缺一不可:<option disabled selected>请选择</option> - 自定义高度或边框颜色时,要同步覆盖
.form-control:focus和.form-label的transform值,否则上浮位置偏移
动态回填数据时 :not(:placeholder-shown) 不会自动激活
编辑页从 API 拿到数据后执行 input.value = "xxx",但 placeholder 还在 DOM 里,CSS 状态不会变——标签仍悬在顶部,像没输过一样。
- 必须手动触发
input.dispatchEvent(new Event('input', { bubbles: true })) - 对
textarea同样适用;select则需设置selectedIndex并派发change事件 - 不要只改
value属性,不派发事件 → CSS 状态永远不同步 - Vue/React 等框架绑定值时,也要确保对应事件被触发(例如 Vue 中用
v-model通常没问题,但直接操作 DOM 就得补事件)
transition 动画细节决定体验是否自然
标签上浮突兀、边框变色断裂、字体闪一下,往往是因为这三处 transition 没对齐。
-
label的transform和font-size必须加transition,否则上浮生硬 -
input的border-color变化也要加transition,否则聚焦反馈断层 -
label的opacity建议设为0.6 → 1渐变,避免纯黑字体在浅背景上“啪”一下出现 - 禁止写
transition: all .2s—— 它会让width、height等无关属性也动画,引发渲染抖动
真正难的不是写第一版浮动标签,而是当表单混用原生 input、动态 select、多语言 placeholder、编辑态回填、无障碍焦点管理时,:placeholder-shown 的边界条件和事件派发时机稍有遗漏,整个状态链就断了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











