【JS】高度动态伸缩的输入框

上周工作中在设计图上看到了这样一个textarea框,只有底下一条线,没有高度
【JS】高度动态伸缩的输入框
也就是说输入框高度不是固定的,是由输入内容决定的

思路

看到这个设计想了想没思路,立刻去找度娘,网上主流解决方案有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事件。

以上是 【JS】高度动态伸缩的输入框 的全部内容, 来源链接: utcz.com/a/104467.html

回到顶部