scrollHeight 必须配合 style.height = 'auto' 使用,否则固定高度会干扰计算,导致 textarea 越撑越高、删空不缩回;需用原生 input 事件监听内容变更,并结合 CSS 的 min-height、max-height 和 box-sizing: border-box 控制尺寸。

scrollHeight 为什么必须配合 style.height = 'auto' 才能用
直接写 textarea.style.height = textarea.scrollHeight + 'px' 会越撑越高,甚至删文字也不缩回。这是因为浏览器把上一次设的固定高度当约束条件,导致 scrollHeight 计算失真——尤其在 iOS Safari 和 IE11 上更明显。
每次读 scrollHeight 前,必须先执行 textarea.style.height = 'auto',让元素回归流式布局基准。否则:删空内容后高度卡住、粘贴大段文本底部留白、回车时光标跳动,都是典型表现。
- 别信
offsetHeight或clientHeight:它们不含溢出区域,永远不准 -
scrollHeight包含 padding,但不含 border 和 margin,所以 CSS 必须设box-sizing: border-box - 空内容时
scrollHeight在 Safari 下可能偏小,建议兜底设min-height: 1.2em
input 事件监听比 keyup 更可靠
用 keyup 会漏掉右键粘贴、拖入文本、IME 输入(如中文输入法上屏)、剪切板粘贴等操作;resize 事件则只响应手动拖拽边框,完全无关。
必须绑定原生 input 事件,它覆盖所有内容变更场景。Vue/React 中若用 v-model 或受控组件,也要确保底层 textarea 仍触发原生 input。
立即学习“前端免费学习笔记(深入)”;
- 不要在
focus或change里做高度调整:前者不反映内容变化,后者只在失焦时触发 - 快速连打或粘贴时容易抖动,可用
requestAnimationFrame包一层,比setTimeout(0)更稳 - IE11 对换行符
\r\n的处理和现代浏览器不同,可统一归一化:textarea.value = textarea.value.replace(/\r\n/g, '\n')
min-height 和 max-height 必须走 CSS,不能靠 JS 算
JS 只负责动态设置 style.height,而初始最小高度、最大上限、是否出现滚动条,全靠 CSS 控制。光靠 JS 算高度,空 textarea 会塌成一条线,狂输时又无限拉伸。
CSS 至少要写这三行:min-height: 80px(保证可点击)、max-height: 300px(超限时才滚动)、overflow-y: auto(否则内容被裁切)。
- 别写
height: 100%或flex: 1在textarea上:会和 JS 设置的style.height冲突 -
resize: none和overflow: hidden要显式加:防止用户拖拽破坏自适应逻辑,或隐藏滚动条导致scrollHeight失真 - 字体、
line-height、padding必须和 JS 计算时渲染一致,否则偏差肉眼可见
移动端和老浏览器的兼容性坑点
iOS Safari 在软键盘弹出后、或刚初始化时,scrollHeight 有微小延迟或偏差;IE11 则对换行符计数和空行高度计算不准。
实操中可分情况处理:
- iOS:用
requestAnimationFrame延迟读取,比setTimeout(0)更准,尤其在 ProMotion 120Hz 屏上 - IE11:若必须保留
\r\n(如日志类场景),可补偿高度:const height = textarea.scrollHeight + (isIE11 ? 2 : 0) - 所有环境都建议加防抖:用
debounce包裹调整函数,延迟 50ms 再执行,避免狂敲时频繁重排
最易被忽略的是样式与 JS 脱节——比如 CSS 里写了 font-size: 16px,但 JS 计算时浏览器还没加载完字体,scrollHeight 就不准了。上线前务必在 Chrome DevTools 里关掉 “user agent stylesheet” 查看真实 computed 值。



















