上周工作中在设计图上看到了这样一个textarea框,只有底下一条线,没有高度

也就是说输入框高度不是固定的,是由输出内容决定的

思路

看到这个设计想了想没思路,立即去找度娘,网上支流解决方案有2种:

  • 监听input事件获取textarea的滚动高度,调节款式
  • 属性contenteditableheight:auto的div能够编辑内容,取代textarea

1.通过事件调节高度

<template>  <div class="flexable-textarea">  <div ref="simulateTextarea" :style="{'padding':padding}" class="simulate-textarea">{{ value || '0' }}</div>    <textarea      ref="editTextarea"      :value="value"      :maxlength="maxLength"      :style="{'padding':padding}"      :placeholder="placeholder"      class="edit-textarea"      @input="handleInput($event)">    </textarea>  </div></template><script>/* 高度自适应的textarea */export default {  name: 'FlexableTextarea',  props: {    padding: {      type: String,      default: '20px 0'    },    value: {      type: String,      default: ''    },    maxLength: {      type: Number,      default: 999999    },    placeholder: {      type: String,      default: ''    }  },  watch: {    value(val, oldVal) {      if (val !== oldVal) {        this.check()      }    }  },  mounted() {    this.check()  },  methods: {    // 检测高度    check() {      this.$nextTick(() => {        const textarea = this.$refs.editTextarea        const simulate = this.$refs.simulateTextarea        if (textarea.style.height !== `${simulate.scrollHeight}px`) {          textarea.style.height = `${simulate.scrollHeight}px`        }      })    },    handleInput(event) {      if (event.target.value !== this.value) this.check()      this.$emit('input', event.target.value)    }  }}</script><style lang="scss" scoped>.flexable-textarea {  position: relative;  overflow: hidden;  font-size: 28px;  line-height: 48px;  padding: 0;  .edit-textarea {    box-sizing: border-box;    font-size: inherit;    line-height: inherit;    white-space: pre-wrap;    overflow-wrap: break-word;    display: block;    width: 100%;    height: auto;    min-height: 48px;  }  .simulate-textarea {    box-sizing: border-box;    position: absolute;    left: 0;    top: 0;    z-index: -1;    opacity: 0;    width: 100%;    height: auto;    min-height: 48px;    font-size: inherit;    line-height: inherit;    white-space: pre-wrap;    overflow-wrap: break-word;  }}</style>

HTML构造并不简单,但有人会问为什么不间接获取textarea的高度,还要做个暗藏的div容器把value再复制一遍呢?因为textarea的个性是能够被撑开,但不会本人膨胀,设置款式height:auto在输出很多行后再删除几行,它的高度是不会变的。所以须要借助其余容器拿到scrollHeight,曲线救国。
长处:
兼容性好
毛病:
1.设置高度时局部浏览器有卡顿感。
2.如果组件一开始暗藏再显示,须要手动调用check办法,不够洁净。

2.用div代替textarea

<template>  <div :style="{padding}" class="flex-input-wrapper" @click.stop="onFocus($event)">    <div      ref="flexInput"      :placeholder="placeholder"      class="flex-input"      contenteditable="true"      @input="changeText($event)"    ></div>  </div></template><script>/* 高度自适应的input */export default {  name: 'FlexableInput',  props: {    padding: {      type: String,      default: '20px 0'    },    value: {      type: String,      default: ''    },    maxLength: {      type: Number,      default: 999999    },    placeholder: {      type: String,      default: ''    }  },  watch: {    value(newValue) {      const ele = this.$refs.flexableInput      const innerText = ele.innerText      if (newValue !== innerText) {        this.setValue(newValue)      }    }  },  mounted() {    this.setValue(this.value)  },  methods: {    setValue(value = '') {      if (value.length === 0) return      const _val = value.length < this.maxLength ? value : value.substring(0, this.maxLength)      this.$refs.flexableInput.innerText = _val    },    changeText(event) {      const ele = event.target      let innerText = ele.innerText      if (innerText.length > this.maxLength) {        innerText = innerText.substring(0, this.maxLength)        ele.innerText = innerText        this.keepLastIndex(ele)      }      this.$emit('input', innerText)    },    onFocus(event) {      const input = this.$refs.flexableInput      if (document.activeElement === input) return      this.keepLastIndex(input)    },    // 固定光标到最初    keepLastIndex(obj) {      if (window.getSelection) {        // ie11 10 9 ff safari        obj.focus() // 解决ff不获取焦点无奈定位问题        const range = window.getSelection() // 创立range        range.selectAllChildren(obj) // range 抉择obj下所有子内容        range.collapseToEnd() // 光标移至最初      } else if (document.selection) {        // ie10 9 8 7 6 5        const range = document.selection.createRange() // 创立选择对象        // var range = document.body.createTextRange();        range.moveToElementText(obj) // range定位到obj        range.collapse(false) // 光标移至最初        range.select()      }    }  }}</script><style lang="scss" scoped>.flexable-input {  outline: none;  user-select: text;  cursor: text;  width: 100%;  font-size: 28px;  line-height: 48px;  white-space: pre-wrap;  overflow-wrap: break-word;  &:empty::before {    content: attr(placeholder);    color: #999;  }}</style>

代码更加简略了,也没有前一种计划的毛病。惟一的瑕疵是点击不够灵活,div常常获取不到焦点,因而加上了click事件。