做一个自适应高度的文本框(以vue2举例子)

153 阅读1分钟

使用两个textarea

基本实现

如同下面这部分代码,这里就是用了两个textarea;一个textarea用来展示,一个textarea隐藏起来用来计算文本高度,并将计算后的高度赋予展示的textarea。首先是第一个show-textarea,这个是用来输入的,hidden-textarea就是用来计算高度的。将显示的textarea中的输入内容双向绑定到intervalue属性中,再通过监听input的事件将intervalue值赋给隐藏的textarea中,实现两个textarea内容同步。然后再计算hidden-textarea的scrollheight(这个包含padding,为了让演示更简单这里将padding设置为0),然后再将这个scrollheight传给show-textarea作为他的高度。这样就可以做到show-textarea的高度跟着内容增多而变高。

<template>
  <div id="app">
    <textarea
      ref="textarea"
      class="textarea show-textarea"
      v-model="intervalue"
      @input="handleInput"
    >
    </textarea>
    <textarea
      ref="hiddentextarea"
      class="hidden-textarea textarea"
    >
    </textarea>
  </div>
</template>
<script>
export default {
  name: 'App',
  data() {
    return {
      intervalue: ''
    }
  },
  methods: {
    handleInput(e) {
      this.$refs.hiddentextarea.value = e.target.value;
      this.$refs.textarea.style.height = `${this.$refs.hiddentextarea.scrollHeight}px`
    }
  }
}
</script>
<style>
.textarea {
  box-sizing: content-box;
  resize: none;
  padding: 0;
  position: relative;

}
.show-textarea {
  width: 440px;
  overflow: auto;
}
.hidden-textarea {
  overflow: hidden;
  position: absolute;
  width: 440px;
  height: auto;
}
</style>

如果要设置一个最大高度呢?

上面这种方式只是解决了最基本的要求,如果我们希望给他设置一个最大高度呢?比如像那些组件库,可以设置最多的行数那样子!我们就需要设置行高和行数,并且根据输入内容的长度限制textarea的滚动条状态。例如下面的这个样子。
这里增加的代码是通过showScroll属性来控制是否允许滚动条展示,以及在高度达到属性row的高度以后就定死show-textarea的高度为row * lineheight。

<template>
  <div id="app">
    <textarea
      ref="textarea"
      class="textarea show-textarea"
      :class="[showScroll ? 'show-scorll' : 'hide-scorll']"
      v-model="intervalue"
      @input="handleInput"
    >
    </textarea>
    <textarea
      ref="hiddentextarea"
      class="hidden-textarea textarea"
    >
    </textarea>
  </div>
</template>

<script>

export default {
  name: 'App',
  data() {
    return {
      intervalue: '',
      row: 5,
      showScroll: false,
    }
  },
  methods: {
    handleInput(e) {
      this.$refs.hiddentextarea.value = e.target.value;
      this.showScroll = false
      if (this.$refs.hiddentextarea.scrollHeight < this.row * 22) {
        this.$refs.textarea.style.height = `${this.$refs.hiddentextarea.scrollHeight}px`
       } else {
         this.showScroll = true
         this.$refs.textarea.style.height = `${this.row * 22}px`
       }
    }
  }
}
</script>

<style>
.textarea {
  box-sizing: content-box;
  resize: none;
  line-height: 22px;
  position: relative;
  padding: 0;
}
.show-textarea {
  width: 440px;
  overflow: auto;
}
.show-scorll {
  overflow: auto;
}
.hide-scroll {
  overflow: hidden;
}
.hidden-textarea {
  overflow: hidden;
  position: absolute;
  width: 440px;
  height: auto;
}
</style>

如果最后还要加一个初始高度呢?

这样的话只需要在最上面的基础上,在style里面加上一点点,再在handleInput方法里面加一点点的处理就好了。直接看代码。
首先在style里面加上height: 66px,这是3行的初始高度。

.textarea {
  box-sizing: content-box;
  resize: none;
  line-height: 22px;
  position: relative;
  padding: 0;
  height: 66px;
}

然后再改一下handleInput方法,当hide-textarea的内容高度大于66px的时候才执行高度变化的部分,如果小于等于66px则直接将高度定为66px。

handleInput(e) {
  this.$refs.hiddentextarea.value = e.target.value;
  this.showScroll = false
  if (this.$refs.hiddentextarea.scrollHeight > 66) {
    if (this.$refs.hiddentextarea.scrollHeight < this.row * 22) {
      this.$refs.textarea.style.height = `${this.$refs.hiddentextarea.scrollHeight}px`
    } else {
      this.showScroll = true
      this.$refs.textarea.style.height = `${this.row * 22}px`
    }
  } else {
    this.$refs.textarea.style.height = `${66}px`
  }
}