为何Textarea高度每次递减2px?仿通讯框高度异常排查
我希望实现一款类似即时通讯软件的textarea组件:默认1行高度,文本溢出时自动扩展至2-4行。为此编写了如下代码:
function onInputHandle() { textarea.style.height = textarea.scrollHeight + "px"; }
该代码在文本行数增加时表现正常,但删除字符使文本不再溢出时,高度无法直接恢复至默认值(例如34px),而是每次递减2px,逐步降至34px,相关运行日志如下:
// 添加字符时 textarea.style.height: 34px textarea.scrollHeight: 34 ------------------- textarea.style.height: 34px textarea.scrollHeight: 34 // textarea溢出,新增一行 ------------------- textarea.style.height: 55px textarea.scrollHeight: 55 ------------------- // 删除字符后不再溢出,本应恢复为34px,却逐步递减2px ------------------- textarea.style.height: 53px textarea.scrollHeight: 51 ------------------- textarea.style.height: 51px textarea.scrollHeight: 49 ------------------- textarea.style.height: 49px textarea.scrollHeight: 47 ------------------- textarea.style.height: 47px textarea.scrollHeight: 45 ------------------- textarea.style.height: 45px textarea.scrollHeight: 43 ------------------- textarea.style.height: 43px textarea.scrollHeight: 41 ------------------- textarea.style.height: 41px textarea.scrollHeight: 39 ------------------- textarea.style.height: 39px textarea.scrollHeight: 37 ------------------- textarea.style.height: 37px textarea.scrollHeight: 35 ------------------- textarea.style.height: 35px textarea.scrollHeight: 34 ------------------- textarea.style.height: 34px textarea.scrollHeight: 34 ------------------- textarea.style.height: 34px textarea.scrollHeight: 34
可复现问题的完整代码示例
JavaScript
const ta = document.querySelector(".ta"); ta.addEventListener("input", () => { console.log(`textarea.style.height: ${ta.offsetHeight}`); console.log(`textarea.scrollHeight: ${ta.scrollHeight}`); console.log("-------------------"); ta.style.height = `${ta.scrollHeight}px`; });
CSS
.ta { /* height: 34px; */ resize: none; }
HTML
<textarea class="ta"></textarea>
请问该异常现象的原因是什么?
这个问题的核心在于**scrollHeight的计算逻辑和当前高度设置方式不匹配**:
当textarea被手动设置为高于默认值的高度(比如日志里的55px)后,即便删除字符让内容只需要1行高度,浏览器计算
scrollHeight时会受当前元素高度影响——此时scrollHeight不会直接返回内容的真实高度(34px),而是返回「当前元素高度减去未被内容填充的多余空间」。由于textarea默认带有上下边框(总高度通常为2px),每次将高度设置为scrollHeight时,相当于把元素高度削减了2px。你没有在计算新高度前重置textarea的高度为默认状态,导致浏览器无法立刻算出内容的真实高度。当前逻辑是直接基于当前的
scrollHeight更新高度,而当元素高度大于内容高度时,scrollHeight会返回一个介于「内容真实高度」和「当前元素高度」之间的值,于是就出现了每次删除字符,高度递减2px的情况,直到最终回到内容的真实高度。
如果要解决这个问题,只需要在获取scrollHeight前,先把textarea的高度重置为auto或者初始默认值,让浏览器重新计算内容的真实高度,再设置新高度即可。
内容的提问来源于stack exchange,提问作者b3rry

