表格列宽莫名膨胀到 150px?罪魁祸首是 input 的隐式 size=20
某天在做一个带筛选行的数据表格,每列表头下面放了一个 <input> 用于本列过滤。布局用 width: max-content 让表格跟着内容自动伸缩,<colgroup> 给固定列指定宽度。看起来很标准,结果上线一跑——每一列都至少 150px,完全无视 colgroup 里写的窄宽,给 input 加 w-full / width: 100% 也没任何效果,把 input 从 DOM 里删掉,列宽瞬间缩回正常。
这个坑卡了我好一阵子,根因相当反直觉。
现象
- 所有含筛选
<input>的列,最小宽度强制约 150px,无法更窄 <table style="width: max-content">本应按内容收缩,但实际宽度远大于文字长度- 给 input 加
className="w-full"毫无改善 - 删掉 input,列立即变窄,确认 input 是元凶
根因
HTML <input type="text"> 有一个名为 size 的属性,默认值是 20(字符数),对应约 150–180px(取决于字体)。这个值控制的是 input 的内在尺寸(intrinsic size),参与浏览器的 CSS content-box 尺寸计算。
当表格使用 width: max-content 或自动布局时,浏览器会测量每个单元格子元素的内在宽度,取其最大值作为列宽基准。input 那个 20 字符的内在宽度直接把列宽撑到了 ~150px,而且优先级高于外部 CSS。
给 input 加 w-full / width: 100% 只会让它在列宽已定之后撑满单元格,但浏览器在计算列宽时用的是 input 的内在宽度——顺序是先量再撑,w-full 对列宽计算毫无影响。
正解
设置 size={1}(或 HTML 里写 size="1"),把 input 的内在宽度压到约 8–10px,使其不再主导列宽计算;再配合 w-full 让 input 跟随列宽拉伸。
修复前(异常)
<th>
<input
type="text"
className="w-full border rounded px-2 py-1"
value={filter[col.key] ?? ''}
onChange={e => setFilter(col.key, e.target.value)}
/>
</th>列宽死锁 ≥150px。
修复后
<th>
<input
type="text"
size={1}
className="w-full border rounded px-2 py-1"
value={filter[col.key] ?? ''}
onChange={e => setFilter(col.key, e.target.value)}
/>
</th>size={1} 把内在宽度压到接近零,w-full 再把它撑满单元格,列宽完全由 colgroup 或内容驱动。
size={1} 负责消除内在尺寸对列宽的干扰;w-full 负责在列宽确定后让 input 填满单元格。两者缺一不可,顺序不影响,但都得写。
如果你在用 width: max-content 做弹性列宽表格,还需要注意:
truncate(即overflow: hidden)在 max-content 测量阶段会让单元格收缩到 0,改用whitespace-nowrap只截断换行,不影响宽度测量w-full加在单元格内层容器上会和truncate打架,要分层处理
一句话外卖
<input>的size属性默认 20,它的内在尺寸参与表格列宽计算且优先级极高,放进筛选行前必须显式设size={1}。