vue-textarea 自适应高度

vue

需求简介

一个搜索页面,上面输入框,下面列表展示搜索到的结果。

重点是:产品要求搜索框默认显示一行,当输入的文字超过一行时,输入框的高度会随着改变,直到输入完毕。

解决思路设想

本想利用textarea实现,但textarea不支持自适应高度,而是定好高度或者是行数之后,超出部分就会显示滚动条。只能另想。

根据需求,首先想到了张鑫旭伪类匹配列表数目实现微信群头像CSS布局的技巧一文提到的文字多字号自动变小的技巧,但仔细一琢磨,不行。这个是根据内容元素的个数,进行处理,而这儿是输入框,没有内容元素。

后面想到可以利用html属性contenteditable="true",加在div上让其可编辑来模拟自适应高度。可是需要在vue中双向绑定实现,这个不是很好处理。

后面想到利用textarea的row属性,根据输入内容的长度控制row的值,为1-n行,但这个似乎不是很智能,因为多少个字体一行不一定,英文、中文、数字的宽度不一致,而且row属性在每个浏览器中的表现不一致。

最后利用textarea,监听change事件,让其高度=其滚动条高度,来达到高度自适应。

没想到最后还是利用了textarea。

实现

参考:textarea如何实现高度自适应?

util.js

/**

* 文本框根据输入内容自适应高度

* @param {HTMLElement} 输入框元素

* @param {Number} 设置光标与输入框保持的距离(默认0)

* @param {Number} 设置最大高度(可选)

* @callback {Function} 设置回调函数(可选)

*/

export const autoTextarea = function (elem, extra, maxHeight, callback) {

extra = extra || 0;

var isFirefox = !!document.getBoxObjectFor || \'mozInnerScreenX\' in window,

isOpera = !!window.opera && !!window.opera.toString().indexOf(\'Opera\'),

addEvent = function (type, callback) {

elem.addEventListener ?

elem.addEventListener(type, callback, false) :

elem.attachEvent(\'on\' + type, callback);

},

getStyle = elem.currentStyle ? function (name) {

var val = elem.currentStyle[name];

if (name === \'height\' && val.search(/px/i) !== 1) {

var rect = elem.getBoundingClientRect();

return rect.bottom - rect.top -

parseFloat(getStyle(\'paddingTop\')) -

parseFloat(getStyle(\'paddingBottom\')) + \'px\';

};

return val;

} : function (name) {

return getComputedStyle(elem, null)[name];

},

minHeight = parseFloat(getStyle(\'height\'));

elem.style.resize = \'none\';

var change = function () {

var scrollTop, height,

padding = 0,

style = elem.style;

if (elem._length === elem.value.length) return;

elem._length = elem.value.length;

if (!isFirefox && !isOpera) {

padding = parseInt(getStyle(\'paddingTop\')) + parseInt(getStyle(\'paddingBottom\'));

};

scrollTop = document.body.scrollTop || document.documentElement.scrollTop;

elem.style.height = minHeight + \'px\';

if (elem.scrollHeight > minHeight) {

if (maxHeight && elem.scrollHeight > maxHeight) {

height = maxHeight - padding;

style.overflowY = \'auto\';

} else {

height = elem.scrollHeight - padding;

style.overflowY = \'hidden\';

};

style.height = height + extra + \'px\';

scrollTop += parseInt(style.height) - elem.currHeight;

document.body.scrollTop = scrollTop;

document.documentElement.scrollTop = scrollTop;

elem.currHeight = parseInt(style.height);

callback(parseInt(style.height));

};

};

addEvent(\'propertychange\', change);

addEvent(\'input\', change);

addEvent(\'focus\', change);

change();

};

export const debounce = function (func, delay) {

let timer;

return function (...args) {

if (timer) {

clearTimeout(timer);

}

timer = setTimeout(() => {

func.apply(this, args);

}, delay || 500);

}

}

说明:由于下面是列表,需要计算高度,为了避免重新再去获取高度,所以加了个回调方法,把高度回调出去。

vue-search.vue

<template>

<div class="search-list">

<div>

<textarea ref="textarea" v-model="keywords" :maxlength="keywordsMax" @change="searchChange"></textarea>

<span class="clear" @click="clearKeywords">x</span>

</div>

<div class="list" ref="list">

<ul>

<li v-for="item in list" :key="item.id">

<span class="icon"></span>

<dl>

<dt>{{item.title}}</dt>

<dd>{{item.desc}}</dd>

</dl>

</li>

</ul>

</div>

</div>

</template>

<script>

import { debounce, autoTextarea } from \'@/util.js\';

let rootFontSize = parseFloat(document.documentElement.style.fontSize);

export default {

data () {

return {

keywordsMax: 128,

keywords: \'\',

list: []

}

},

mounted () {

this.$nextTick(() => {

let textarea = this.$refs.textarea;

textarea.focus();

let prevHeight = 65;

textarea && autoTextarea(textarea, 5, 0, (height) => {

height += 20;

if (height !== prevHeight) {

prevHeight = height;

let rem = height / rootFontSize;

this.$refs.list.style.height = `calc(100% - ${rem}rem)`;

}

});

})

},

methods: {

clearKeywords () {

this.keywords = \'\';

this.list = [];

let textarea = this.$refs.textarea;

let height = 40;

let rem = height / rootFontSize;

textarea.style.height = `${rem}rem`;

rem = (height + 20) / rootFontSize;

this.$refs.list.style.height = `calc(100% - ${rem}rem)`;

textarea.focus();

},

searchChange: debounce(function () {

let trim = this.keywords.trim();

if (!trim) {

this.list = [];

return;

}

const params = {

keywords: this.keywords

}

// 调api ...

})

}

}

</script>

补充div模拟textarea自适应

<style>

.textarea{

width: 400px;

min-height: 20px;

max-height: 300px;

_height: 120px;

margin-left: auto;

margin-right: auto;

padding: 3px;

outline: 0;

border: 1px solid #a0b3d6;

font-size: 12px;

line-height: 24px;

padding: 2px;

word-wrap: break-word;

overflow-x: hidden;

overflow-y: auto;

border-color: rgba(82, 168, 236, 0.8);

box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);

}

</style>

<div class="textarea" contenteditable="true"></div>

以上是 vue-textarea 自适应高度 的全部内容, 来源链接: utcz.com/z/374945.html

回到顶部