摘要:本文描述了一种实现随着文本输入或删除能自动改变高度适应文字量的简单文本编辑框的方法,效果入下图(没有富文本格式,只有基本的换行、空格等格式)。主要思路是建立两个textarea
元素,隐藏其中一个,随着文字输入检查隐藏的textarea
元素的滚动高度,把这个高度值赋给显示的textarea
。
起因
前段时间做了一个类ppt
的在线编辑器,当然功能比ppt
简单很多。其中在插入文字的时候由于输入完成之后要有基本的换行,空格之类的格式,因此用<textarea>
元素是比较合适的。但是在textarea
元素内输入文字的时候,如果内容比较多,超出元素高度的时候,就会出现滚动条,这显然是不符合一个类ppt
编辑器对文字元素的要求的。当然简单一点可以让不去管它,提供一个功能让用户自己去调整文字框的大小,不过既然做了总想做好一点。于是通过搜索和摸索实现了一个能够随着文字输入或删除,高度能够自适应改变的文本框。
实现思路和过程
初始方案
一开始的基本思路就是每当在textarea元素的内容发生改变之后,获取textarea
的scrollHeight
,然后让textarea
的style.height
等于这个值。于是初版代码大约是这样(我用的vue
框架,其他框架或者不用框架应该也是类似):
<textarea type="text" @focus="textFocus" v-model="content" ref="input" :style="{height: teaxtAreaHeight}" rows="1"
style="overflow-y: hidden;">
</textarea>
export default {
data() {
content: '',
teaxtAreaHeight: ''
},
methods: {
textFocus() {
document.addEventListener('keyup', () => {
this.$nextTick(() => {
const height = `${(this.$refs.input).scrollHeight}px`;
if (height !== this.teaxtAreaHeight) {
this.teaxtAreaHeight = height;
}
});
});
}
}
}
界面上的textarea
元素内容绑定了content
变量,元素高度height
绑定变量textAreaHeight
,初始是默认高度。overflow-y
属性是hidden
,为的是不出现滚动条。当文本框获得焦点时,执行textFocus
,监听键盘输入事件,获取文本框的scrollHeight
也就是滚动高度,如果滚动高度和现在文本框的高度不一样,说明因为文字输入导致了文本内容高度与文本框高度不同,则改变文本框高度teaxtAreaHeight
。其中$nextTick
方法是vue
中用于等待下一次dom
重新渲染后再执行其中的代码,因为文字输入后文本框的scrollHeight
要等到dom
重新渲染才会变化。这样就可以实现随着文字输入,文本框高度自动适应文本内容高度,不会有滚动条了。
初始方案的问题
上面这个方案虽然实现了随着文字输入高度自动增加来适应文本内容,但是有个问题,就是不能够随着文字的删除来自动减小高度适应删减后的文本高度。因为一旦给textarea
设置了较大的高度height
,那么在文字删除后scollHeight
也不会重新减小了!
改进
为了解决上面的问题,加入第二个textarea
。这个textarea
始终是隐藏的,与显示的textarea
绑定了同样的文本内容,不改变该textarea
的height
,这样scollHeight
就会随着文字内容高度的变化而变化,这样由它作为一把高度的尺子,以它的scollHeight
为标准去改变显示textarea
的height
:
<textarea @focus="textFocus" type="text" v-model="content" ref="input" :style="{height: teaxtAreaHeight}"
rows="1" style="overflow-y: hidden;">
</textarea>
<textarea style="position: absolute; visibility:hidden;" ref="heightRuler" v-model="content" rows="1"></textarea>
export default {
data() {
content: '',
teaxtAreaHeight: ''
},
methods: {
textFocus() {
document.addEventListener('keyup', () => {
this.$nextTick(() => {
const height = `${(this.$refs.heightRuler).scrollHeight}px`;
if (height !== this.teaxtAreaHeight) {
this.teaxtAreaHeight = height;
}
});
});
}
}
}
vue优化版
思路还是之前那个思路,不过代码可以变得更加优雅。之前的做法是监听键盘事件,在vue中还可以直接通过watch监听value值来计算高度,也可以通过计算属性的set方法在value发生改变的时候计算高度