1. 程式人生 > 程式設計 >Vue中textarea自適應高度方案的實現

Vue中textarea自適應高度方案的實現

目錄
  • 隱藏的問題
  • 解決自適應高度的方案

先給方案,棧有需求的同學可以直接下載vue-awesome-textarea

隱藏的問題

拋開原生,框架的大部分UI庫都支援自適應textarea高度功能,但普遍都忽略了一個功能,就是自適應高度的回顯。

使用這些庫的時候,我們很容易的在textarea中鍵入內容,超出範圍時會自動延展一行,保證內容高度的自適應。當我們提交內容,在其它頁面使用同樣的UI來渲染時,麻煩的就來了,有些UI庫是不支援自適應回顯的,這就需要我們通過行高、行數甚至高度之間的計算得出一個基值,從而實現回顯。

解決自適應高度的方案

常見得方案有兩種,一種是在頁面地“邊遠地區”新增一個ghost dom來模擬輸入換行,這個dom的可能是editable屬性為true的div或者是一個一摸一樣得textarea。

以element-ui的input元件舉例,當我們在元件內輸入值時,會呼叫resizeTextarea方法

resizeTextarea() {
 if (this.$isServer) return;
 const { autosize,type } = this;
 if (type !== 'textarea') return;
 if (!autosize) {
  this.textareaCalcStyle = {
   minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
  };
  return;
 }
 const minRows = autosize.minRows;
 const maxRows = autosize.maxRows;

 this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea,minRofQSmLdzCF
ws,maxRows);www.cppcns.com }

當設定了autosize為true則textarea設為自適應高度。此時textarea的高度會通過calcTextareaHeight方法實時計算。

export default function calcTextareaHeight(
 targetElement,minRows = 1,maxRows = null
) {
 if (!hiddenTextarea) {
  hiddenTextarea = document.createElement('textarea');
  document.body.appendChild(hiddenTextarea);
 }

 let {
  paddingSize,borderSize,boxSizing,contextStyle
 } = calculateNodeStyling(targetElement);

 hiddenTextarea.setAttribute('style',`${contextStyle};${HIDDEN_STYLE}`);
 hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';

 let height = hiddenTextarea.scrollHeight;
 const result = {};

 if (boxSizing === 'border-box') {
  height = height + borderSize;
 } else if (boxSizing === 'content-box') {
  height = height - paddingSize;
 }

 hiddenTextarea.value = '';
 let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

 if (minRows !== null) {
  let minHeight = singleRowHeight * minRows;
  if (boxSizing === 'border-box') {
   minHeight = minHeight + paddingSize + borderSize;
  }
  height = Math.max(minHeight,height);
  result.minHeight = `${ minHeight }px`;
 }
 if (maxRows !== null) {
  let maxHeight = singleRowHeight * maxRows;
  if (boxSizing === 'border-box') {
   maxHeight = maxHeight + paddingSize + borderSize;
  }
  height = Math.min(maxHeight,height);
 }
 result.height = `${ heighwww.cppcns.com
t }px`; hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea); hiddenTextarea = null; return result; };

我們可以看到

if (!hiddenTextarea) {
  hiddenTextarea = document.createElement('textarea');
  document.body.appendChild(hiddenTextarea);
}

element-ui建立了一個textarea的dom,通過calculateNodeStyling方法將真正的textarea的樣式複製給hiddenTextarea(overflow不同步,真正的textarea是為hidden)。接著監聽textarea的輸入值,同步給hiddenTextarea。同時將hiddenTextarea的scrollHeight同步給textarea的高度,最後再將dom銷燬掉。

關於樣式的同步,element這裡用了getComputedStyle和getPropertyValue這兩個API。當然,如果你自己封裝的話,也可以使用前處理器的mixin。

第二種方案與第一種方案類似,不過不會建立額外的dom。以開頭的vue-awesome-textarea舉例:

init() {
  this.initAutoResize()
},initAutoResize () {
  this.autoResize && this.$nextTick(this.calcResize)
}

在頁面mounted或者內容變動且開啟自適應高度autoResize的時候,執行this.calcResize方法。

calcResize() {
 this.resetHeight()
 this.calcTextareaH()
},resetHeight() {
 this.height = 'auto'
},calcTextareaH() {
 let contentHeight = this.calcContentHeight()
 this.height = this.calcHeightChange(contentHeight) + 'pxfQSmLdzCF'
 if (this.needUpdateRows(contentHeight)) {
  this.updateRows(contentHeight)
 }
 this.oldContentHeight = contentHeight
},calcContentHeight () {
 const { paddingSize } = this.calcNodeStyle(this.$el)
 return this.$el.scrollHeight - paddingSize
},

resetHeight()是來初始化textarea的高度,預設為auto。calcTextareaH()方法是用來計算內容區域的高度(textarea的scrollHeight減去padding的高度),同時將計算好的高度實時同步給textarea的高:
this.height = this.calcHeightChange(contentHeight) + 'px'

相比方案一,這個方案採用的思路相同(動態修改高度),但是減少了額外的dom建立和銷燬的過程。
此外,vue-awesome-textarea還支援在自適應的過程中回撥行數,可以更好的支援資料回顯。實現的方法也很簡單:

computed: {
 ...
 oneRowsHeight() {
  return this.calcContentHeight() / Number(this.rows) || 0
 }
 ...
}

在computed中我們計算出單行的高度,同時在執行this.calcTextareaH()方法時我們記錄下內容高度:

this.oldContentHeight = contentHeight

接著我們會比對是否存在新增行操作,一旦新增則新的內容高度和老的內容高度會不同:

needUpdateRows(newContentHeight) {
  return this.oldContentHeight !== newContentHeight
},

此時我們會把最新的行高emit到元件外部:

updateRows(contentHeight) {
  this.$emit('getRows',Math.round(contentHeight / this.oneRowsHeight))
}

到此這篇關於Vue中textarea自適應高度方案的實現的文章就介紹到這了,更多相關Vue textarea自適應內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!