基于原生JS实现随文本内容自适应高度的textarea(无jQuery)
Hey there! 作为刚从后端转过来入门原生JS的开发者,你遇到的这个问题其实非常典型——动态生成的元素没法绑定页面加载时就定义好的事件,毕竟代码执行的时候,那个textarea还不存在呢!别担心,咱们一步步来解决它~
问题根源
你原来的自适应高度代码是在页面加载时执行的,这时候document.getElementById('reply-message')根本找不到元素,自然没法给它绑定keyup事件。只有当用户点击按钮创建textarea之后,我们才能给这个新生成的元素绑定事件。
解决方案1:创建textarea后立即初始化自适应逻辑(推荐新手)
最直观的方法就是把自适应高度的逻辑封装成一个函数,每次创建完textarea之后,立刻调用这个函数给它绑定事件。这样就能保证事件只绑定到已经存在的元素上。
修改后的完整代码如下:
JavaScript部分
var replyBtns = document.querySelectorAll('[id=rep]'); for(var i = 0; i < replyBtns.length; i++) { replyBtns[i].addEventListener('click', function(e) { e.preventDefault(); // 移除已存在的回复框 var existingTextarea = document.getElementById("reply-message"); if (existingTextarea) { existingTextarea.parentNode.removeChild(existingTextarea); // 顺便移除对应的隐藏div,避免DOM冗余 var hiddenDiv = document.querySelector('.hiddendiv'); if (hiddenDiv) { hiddenDiv.parentNode.removeChild(hiddenDiv); } } // 创建新的textarea var replyBox = document.createElement('textarea'); replyBox.setAttribute('id', 'reply-message'); replyBox.setAttribute('placeholder', 'Reply'); this.parentNode.insertAdjacentElement('beforeend', replyBox); // 关键:给新创建的textarea初始化自适应逻辑 setupAutoResize(replyBox); }, false); } // 封装自适应高度的函数 function setupAutoResize(textarea) { // 创建隐藏的克隆div let hiddenDiv = document.createElement('div'); hiddenDiv.classList.add('hiddendiv'); document.body.appendChild(hiddenDiv); // 定义更新高度的逻辑 function updateHeight() { let content = textarea.value; // 加上换行是为了让高度计算更准确,避免内容顶到边界 hiddenDiv.innerHTML = content + '\n\n'; textarea.style.height = hiddenDiv.getBoundingClientRect().height + 'px'; } // 绑定输入事件:keyup + input(覆盖粘贴、拖拽等非键盘输入场景) textarea.addEventListener('keyup', updateHeight, false); textarea.addEventListener('input', updateHeight, false); // 初始化一次高度,确保刚创建时就和占位符/初始内容匹配 updateHeight(); }
CSS部分(保持你原来的即可)
textarea#reply-message { display: block; border: none; color:#306654; font-size:120%; padding: 5px 10px; line-height: 20px; width:550px; height: 20px; border-radius: 6px; overflow: hidden; resize: none; } .hiddendiv { position: absolute; left: -9999px; visibility: hidden; white-space: pre-wrap; width: 550px; height: 20px; font-size: 120%; padding: 5px 10px; word-wrap: break-word; overflow-wrap: break-word; }
解决方案2:事件委托(适合多动态元素场景)
如果你的页面会频繁创建/删除textarea,或者有多个类似的动态元素,用事件委托会更高效。原理是利用事件冒泡,把事件绑定到父元素(甚至document)上,当事件触发时判断目标元素是不是我们的textarea。
示例代码:
// 页面加载时就绑定事件到document document.addEventListener('keyup', handleTextareaResize); document.addEventListener('input', handleTextareaResize); function handleTextareaResize(e) { // 只处理id为reply-message的textarea if (e.target.id !== 'reply-message') return; let txt = e.target; let hiddenDiv = document.querySelector('.hiddendiv'); // 如果隐藏div不存在,创建它 if (!hiddenDiv) { hiddenDiv = document.createElement('div'); hiddenDiv.classList.add('hiddendiv'); document.body.appendChild(hiddenDiv); } let content = txt.value; hiddenDiv.innerHTML = content + '\n\n'; txt.style.height = hiddenDiv.getBoundingClientRect().height + 'px'; }
这种方法不需要每次创建textarea都绑定事件,一次绑定就能处理所有未来生成的同类型元素,适合更复杂的场景。
小优化建议
- 确保
.hiddendiv的样式和textarea#reply-message完全一致(你已经做到了),这样高度计算才会准确。 - 移除textarea时顺便删掉对应的隐藏div,避免DOM里积累无用元素(解决方案1里已经加了这个逻辑)。
- 绑定
input事件,因为keyup覆盖不了粘贴、鼠标拖拽输入等场景。
这样修改之后,你的textarea就能正常实现自适应高度啦!
内容的提问来源于stack exchange,提问作者Hassan Baig
相关产品推荐
相关产品推荐

