需求简介
一个搜索页面,上面输入框,下面列表展示搜索到的结果。
重点是:产品要求搜索框默认显示一行,当输入的文字超过一行时,输入框的高度会随着改变,直到输入完毕。
解决思路设想
本想利用textarea实现,但textarea不支持自适应高度,而是定好高度或者是行数之后,超出部分就会显示滚动条。只能另想。
根据需求,首先想到了张鑫旭一文提到的文字多字号自动变小的技巧,但仔细一琢磨,不行。这个是根据内容元素的个数,进行处理,而这儿是输入框,没有内容元素。
后面想到可以利用html属性contenteditable="true"
,加在div上让其可编辑来模拟自适应高度。可是需要在vue中双向绑定实现,这个不是很好处理。
后面想到利用textarea的row属性,根据输入内容的长度控制row的值,为1-n行,但这个似乎不是很智能,因为多少个字体一行不一定,英文、中文、数字的宽度不一致,而且row属性在每个浏览器中的表现不一致。
最后利用textarea,监听change事件,让其高度=其滚动条高度,来达到高度自适应。
没想到最后还是利用了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
x
- { {item.title}}
- { {item.desc}}