![](https://box.kancloud.cn/6651c43748df4921aaec6ff2a2318ab3_695x285.png)
如图,微博的输入框是高度自适应的,随着内容的增多,高度越来越高。
这个技术的关键点在于,**如何计算内容所撑开的高度,然后动态的设置textarea的height.**
其次,如果根据内容动态设置height, 那当内容只有一行的时候,也给textarea设置计算的height吗?肯定不行啊,所以要考虑到**最小高度的设置**。
最终结果[calcTextareaHeight.js](https://github.com/jvsheng/utils/blob/master/From-element/calcTextareaHeight.js)
**整体思路**
1. 我们通过新增一个textarea节点, 让它的样式和内容和需要计算高度的那个textarea基本没差.
2. 为它加一个hidden样式,不能因为新增节点,导致布局发生改变。
3. 通过scrollHeight获取高度,考虑box-sizing属性会造成一些变异,需要计算一下真实需要返回的height值。
4. 考虑最小高度和最大高度,我们通过minRows和maxRows所以,需要考虑计算单行文本的高度。
**DOM优化**
如果去试了微博的输入框,会发现,当回车的时候,显示上去了一点,然后才取消滚动条撑开textarea,显得不是非常的流畅。
Vue的$nextTick就刚好可以解决这个问题。在组件中,监听input事件,**如果值value没有改变,直接返回,不要做过多的计算**。否则,在$nextTick中,计算动态高度。这样,在textarea的value发生了数据改变后,会在下次DOM渲染前计算好高度,这样就会显得非常流畅了。
**获取文本高度**
我们要完成的是一个,支持最小高度和最大高度的textarea组件,所以,通过获取height属性来获取高度肯定是不行的,需要用到[scrollHeight](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollHeight)。scrollHeight返回的是content + padding的高度。
组件兼容性的考虑,要想到box-sizing这种情况,返回真实的height值。
如果为border-box, 返回的应该是 scrollHeight + border高度。
如果为content-box, 返回的应该是 scrollHeight - padding高度。
所有高度判断的代码都写在一堆,中间还夹杂了style样式的计算?no!!!
看下大神对于函数的拆分
```js
function calcNodeStyling(node) {
var style = window.getComputedStyle(node);
var boxSizing = style.getPropertyValue('box-sizing');
var paddingSize = (
parseInt(style.getPropertyValue('padding-top')) +
parseInt(style.getPropertyValue('padding-bottom'))
);
var borderSize = (
parseInt(style.getPropertyValue('border-top')) +
parseInt(style.getPropertyValue('border-bottom'))
);
var contextStyle = CONTEXT_STYLE.map(function(name) {
return name + ':' + style.getPropertyValue('name');
}).join(';');
return {
'boxSizing': boxSizing,
'paddingSize': paddingSize,
'borderSize': borderSize,
'contextStyle': contextStyle
};
}
```
**最小高度与最大高度**
```js
function calcTextareaHeight(node, minRows, maxRows) {
//`````````````````
hiddenTextarea.value = '';
var singleRowHieght = hiddenTextarea.scrollHeight - calStyle.paddingSize;
if (minRows) {
var minHeight = singleRowHeight * minRows;
if (calStyle.boxSizing === 'border-box') {
minHeight = minHeight + calStyle.paddingSize + calStyle.borderSize;
}
height = Math.max(minHeight, height);
}
if (maxRows) {
var maxHeight = singleRowHeight * maxRows;
if (calStyle.boxSizing === 'border-box') {
maxHeight = maxHeight + calStyle.paddingSize + calStyle.borderSize;
}
height = Math.min(maxHeight, height);
}
return { height: height + 'px' };
}
```
**代码的优化**
1. 对于hidden样式和原有样式,放在了一个数组中,通过map函数和join函数,转换为字符串,不像for循环那样,显得臃肿。
2. 对于计算样式方面,抽离函数出来,然后返回一个对象,将需要返回的信息都放在里面。整个js看起来也显得更简洁了。
3. 写组件要考虑适用性,比如这里如果不考虑box-sizing, 问题就非常严重,有些地方根本没法用。