上周工作中在设计图上看到了这样一个textarea框,只有底下一条线,没有高度
也就是说输入框高度不是固定的,是由输出内容决定的
思路
看到这个设计想了想没思路,立即去找度娘,网上支流解决方案有2种:
- 监听
input
事件获取textarea
的滚动高度,调节款式 - 属性
contenteditable
让height: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事件。