1. 为什么我们需要动态高度的输入框?
不知道你有没有遇到过这种情况:在写一个评论框,或者一个笔记应用的编辑区,用户一开始只打了一行字,输入框高度刚刚好。结果他越写越嗨,内容越来越多,输入框却纹丝不动,最后要么是出现了难看的滚动条,要么是文字跑到框外面去了,用户体验非常糟糕。这就是传统单行 <input> 标签的局限性——它生来就是一条“单行道”,高度是死的,不会因为内容的多少而改变。
所以,“动态高度文本输入框”这个需求就应运而生了。它的核心目标很简单:宽度固定,但高度能像橡皮筋一样,随着用户输入内容的行数自动撑开或收缩。这样既能保持界面布局的整洁,又能给用户无拘无束的书写体验。这个功能在社交应用的评论框、即时通讯的输入框、笔记应用的标题或摘要栏、表单的长文本描述字段等场景下,几乎是刚需。
要实现这个效果,我们通常有两个主流的技术方案在“打架”:一个是改造原生的多行文本域 <textarea>,另一个则是用可编辑的 <div> 来模拟一个输入框。这两种方案我都深度使用过,也踩过不少坑。今天,我就以一个过来人的身份,把这两种方案的里里外外、优缺点以及那些实战中才会遇到的细节,给你掰开揉碎了讲清楚。你会发现,没有绝对的好坏,只有合不合适。
2. 方案一:改造原生 <textarea>(简单直接)
这是我最推荐新手和大多数项目优先考虑的方案。<textarea> 本身就是为多行文本输入而生的,我们只是需要让它“自动长高”而已。
2.1 核心实现原理与代码
它的原理非常直观。<textarea> 有一个 scrollHeight 属性,这个属性代表了包含溢出内容在内的元素内容总高度,而且这个值是一个纯数字(单位是像素)。我们的思路就是:每当用户输入时,先把文本框的高度重置为自动(auto),然后立刻将其高度设置为它的 scrollHeight。这样,它的高度就刚好包裹住所有文本内容了。
下面是一个结合了 Vue 3 组合式 API 的完整示例,我加上了详细的注释:
<template>
<!--
ref: 用于获取DOM元素实例
v-model: 双向绑定数据
rows="1": 设置初始行数为1,作为起始高度
@input: 监听输入事件,触发高度调整
-->
<textarea
ref="textareaRef"
v-model="content"
placeholder="开始你的创作..."
class="auto-resize-textarea"
rows="1"
@input="handleTextareaInput"
/>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 绑定的文本内容
const content = ref('')
// 指向textarea元素的引用
const textareaRef = ref<HTMLTextAreaElement | null>(null)
// 核心的输入处理函数
const handleTextareaInput = () => {
// 如果元素引用不存在,直接返回
if (!textareaRef.value) return
// 1. 先将高度重置为‘auto’,让scrollHeight能计算单行高度
textareaRef.value.style.height = 'auto'
// 2. 将元素的高度设置为它的完整内容高度
textareaRef.value.style.height = `${textareaRef.value.scrollHeight}px`
}
// 可选的:组件挂载时也计算一次初始高度(如果有初始值)
onMounted(() => {
if (content.value && textareaRef.value) {
handleTextareaInput()
}
})
</script>
<style scoped>
.auto-resize-textarea {
/* 固定宽度 */
width: 500px;
/* 最小高度,保证即使没内容也有个好看的框 */
min-height: 60px;
/* 禁用用户手动拖拽改变大小,保持我们程序控制的纯洁性 */
resize: none;
/* 视觉样式 */
padding: 12px 16px;
font-size: 16px;
line-height: 1.5;
border: 1px solid #dcdfe6;
border-radius: 8px;
font-family: inherit;
/* 盒模型很重要:确保padding和border被包含在width/height内 */
box-sizing: border-box;
/* 过渡动画,让高度变化更平滑 */
transition: border-color 0.3s, height 0.2s ease-out;
}
.auto-resize-textarea:focus {
outli

627

被折叠的 条评论
为什么被折叠?



