
如圖,微博的輸入框是高度自適應的,隨著內容的增多,高度越來越高。
這個技術的關鍵點在于,**如何計算內容所撐開的高度,然后動態的設置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, 問題就非常嚴重,有些地方根本沒法用。