textarea高度自适应必须用JavaScript动态设置style.height,因其是替换元素,height:auto无效;需用scrollHeight三步法(设auto→读高度→赋值),并注意iOS兼容性与CSS基础样式对齐。

textarea 高度自适应无法仅靠 CSS 实现,必须用 JavaScript 动态设置 style.height,否则必然出现滚动条、高度卡死或 iOS 跳动。
为什么 height: auto 对 textarea 完全无效
textarea 是替换元素(replaced element),浏览器不按常规盒模型计算其 height: auto。设了也等于没设——它仍按 rows 或默认行高撑开,内容溢出就出滚动条。CSS 中的 min-height 和 max-height 可控上下限,但中间“随内容伸缩”这段,CSS 没有读取文本行数或内容高度的能力。
常见误判来源:
- 把
resize: vertical+overflow-y: auto当成“自适应”,其实只是允许手动拉伸 + 有滚动条 - 看到
field-sizing: content就写上,但它对 textarea 完全不生效(Chrome 123+ 仅实验性支持input[type="number"]) - 在 Flex 容器里给 textarea 加
flex: 1,结果它要么压扁为 0,要么撑满剩余空间——这不是自适应,是占位
必须用 scrollHeight 的三步法
核心逻辑极简,但漏一步就会错:先释放约束,再读真实高度,最后赋值。任何跳过 el.style.height = 'auto' 的写法,都会因旧 height 值干扰导致 scrollHeight 计算偏小。
立即学习“前端免费学习笔记(深入)”;
-
textarea.style.height = 'auto'—— 清除上次设定的固定高度,让 scrollHeight 重新按完整内容计算 -
const h = textarea.scrollHeight—— 此时才是真实内容高度(含 padding,不含 border) -
textarea.style.height = h + 'px'—— 赋值,完成一次更新
注意:offsetHeight 和 clientHeight 永远只返回当前渲染高度,不会包含溢出内容,不能替代 scrollHeight。
iOS Safari 的三个硬性兼容点
iOS 上高度错乱不是代码写错,而是时机和状态没对齐。以下三点缺一不可:
- 监听
compositionstart和compositionend:拼音输入法未上屏时就触发input,但scrollHeight还没更新,需标记“正在输入中”,延迟重算 - 每次更新必须包
requestAnimationFrame:直接赋值会滞后一帧,尤其在粘贴长文本后,“光标已换行,框还没长高”就是这个原因 - 监听
visualViewport的resize事件:键盘弹起瞬间 viewport 高度突变,scrollHeight会短暂失准,必须触发一次重算
样式层面最容易被忽略的四个基础项
JS 逻辑再正确,如果 CSS 没对齐,视觉上依然错位:
-
box-sizing: border-box必须显式声明:textarea 默认是content-box,而项目里其他表单控件多为border-box,不统一会导致宽度/高度计算偏差 -
min-height和max-height必须设置:空内容时若无min-height,textarea 可能塌成一条线;不限max-height,用户粘贴万字文档会把页面顶飞 -
overflow-y: auto必须写:不写则内容被裁切,scrollHeight返回值错误 -
font-family、font-size、line-height必须三者同时声明且推荐用px:不同浏览器对字体回退策略不同,继承缺失会导致行高计算不一致
真正上线可用的方案,从来不是“加个 CSS 属性就完事”,而是 JS 逻辑 + 样式约束 + 移动端状态管理的组合。最常出问题的地方,往往不在 scrollHeight 计算本身,而在父容器的 overflow: hidden、未处理的 \r\n 换行符,或者忘了给 textarea 显式设 box-sizing。


















