直接设width: 0会导致动画跳变,因浏览器将其坍缩为0,内部文字、padding、border全部消失,offsetWidth读数为0,transition插值失去依据;应改用非零收起态如width: 32px,配合overflow: hidden和white-space: nowrap。

为什么直接写 width: 0 会跳变
浏览器把 width: 0 当作“彻底坍缩”状态,内部文字、padding、border 全被压掉,offsetWidth 读出来是 0,transition 插值失去依据。结果就是动画不执行,视觉上“啪”一下弹开。
真正可用的收起态必须是非零且明确:比如 width: 32px(刚好放一个放大镜图标),再配 overflow: hidden 和 white-space: nowrap 防文字换行打断节奏。
-
width: 0、display: none、visibility: hidden都不能参与 transition - 起始值必须是可计算单位:
32px、2rem可以,auto或100%不行 -
transition声明必须写在默认状态规则里,不能只写在:focus或展开类中
用 max-width 替代 width 避开 flex 干扰
在 flex 容器里给 <input> 设 flex-grow: 1 再加 transition: width,动画大概率失效——因为 flex-grow 分配的是动态剩余空间,不是可插值的数值类型。
更稳的路径是放弃 width,改用 max-width 控制伸展上限:
立即学习“前端免费学习笔记(深入)”;
- 初始:
max-width: 40px;overflow: hidden - 展开:
max-width: min(320px, 80vw)(兼顾小屏和留白) - 必须配合
width: 100%,否则max-width不生效 - 这个方案 IE10+ 兼容,且不会被 flex 布局逻辑干扰
移动端点击不到输入框?热区没扩够
收起态 width: 32px 只够放图标,用户点不到 <input> 本体,iOS Safari 尤其容易 focus 失败,键盘根本打不开。
别用 opacity: 0 + pointer-events: none 隐藏 <input>——这会切断焦点链。
- 给
<input>加padding-left: 32px,再用::before绝对定位图标,左侧空白区域也能触发聚焦 - 搜索按钮必须设
flex-shrink: 0,否则小屏下被压缩消失 - 移动端建议把按钮抽成
position: absolute叠在<input>右侧,释放水平空间 - 务必加
box-sizing: border-box,否则padding和border让视觉宽度超出预期
JS 切换类名时动画卡顿的真实原因
点击后立刻 el.classList.add('expanded'),又马上读 el.offsetWidth,会强制同步重排,动画直接中断。这不是 CSS 写错了,而是 JS 测量时机不对。
正确做法有两种:
- 用
getComputedStyle(el).width读值前,包一层requestAnimationFrame延迟一帧 - 更推荐放弃 JS 测量,改用 CSS 原生状态驱动,比如
:focus-within或:has(input:focus)(Chrome/Firefox/Edge 已稳定支持) - 若需兼容老浏览器,可在 JS 中先读一次
el.offsetWidth强制触发布局,再改 class ——但仅限一次,禁止连读连写
真正难的不是写几行 transition,而是控制好宽度变化时内容不换行、图标不位移、焦点不丢失。这些细节全靠 white-space、overflow、box-sizing 和触发时机共同约束。


















