可自適應文本高度的輸入框

可適應文本高度的輸入框組件其實一開始是Ant design裏面就有的,Element-ui也實現了,可是經過看源碼,其實用的都是一個類似的js...node

咱們須要實現的這個組件,能夠指定最小行數(minRows)和最大行數(maxRows),用來限定textarea的高度變化範圍。ui

最終實現的效果

可自適應文本高度的輸入框spa

說一下具體的實現思路吧.net

  1. 在body中添加一個不可見的元素,樣式儘量的和組件輸入框的同樣。當輸入框的值發生變化時,經過這個不可見的元素,去模擬計算這時候組件輸入框文本所佔高度height。
  2. 經過計算出單行高度,得到minRows * singleRowHeightmaxRows *singleRowHeight,這樣就有了一個最大、最小高度範圍。
  3. 最後,經過這三個值:height, minHeight, maxHeight.作一下範圍判斷,就能夠得到組件輸入框的實時高度了。

具體代碼邏輯:code

// 不可見的節點,用來模擬輸入框,計算當前輸入值所佔的高度
let hiddenTextarea;

// 隱藏不可見節點,所使用的樣式
let HIDDEN_STYLE = `
    // ...
`;

// 模擬輸入框,須要考慮模擬哪些樣式
let NODE_STYLE = [
    // ...
];

// 計算輸入框的樣式,padding, border, box-sizing, 以及模擬樣式的值
function calcNodeStyle(targetElement) {
    var styles = window.getComputedStyle(targetElement);
    // ...
    return {nodeStyle,paddingSize,borderSize,boxSizing};
}

function calcTextareaHeight(targetElement, minRows=1, maxRows=null) {
    // ...
    // 增長不可見節點,模擬組件輸入框的樣式
    hiddenTextarea.setAttribute('style', `${nodeStyle};${HIDDEN_STYLE}`);
    
    // 計算出單行文本的高度
    hiddenTextarea.value = '';
    let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

    // 計算當前輸入值所佔高度
    hiddenTextarea.value = targetElement.value || targetElement.placeHolder;
    let height = hiddenTextarea.scrollHeight;
    // ...
    let result = {};
    if(minRows) {
        // 根據單行文本高度,計算最小高度值
    }
    if(maxRows) {
        // 根據單行文本高度,計算最大高度值
    }
    
    result.height = `${height}px`;
    hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
    hiddenTextarea = null;
    return result;
}

scrollHeight

看一下MDN中對於scrollHeight的定義:Element.scrollHeightblog

從下圖中,咱們能夠很直觀的看出,scrollHeight = 內容區域高度(content-height) + paddingSize。不包括border以及margin的值。ip

clipboard.png

在這個組件中,咱們須要去模擬當前輸入框的樣式,去計算當前輸入值會佔多少的高度,就須要用到這個屬性。ci

let HIDDEN_STYLE = `
    height:0 !important;
    overflow:hidden !important;
    position: fixed !important;
    top: -9999px !important;
    left: -9999px !important;
    opacity: 0 !important;
`;

function calcTextareaHeight(targetElement, minRows=1, maxRows=null) {
    // ...
    hiddenTextarea.value = '';
    let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

    hiddenTextarea.value = targetElement.value || targetElement.placeHolder;
    let height = hiddenTextarea.scrollHeight;
    
    if(boxSizing === 'border-box') {
        height = height + borderSize;
    } else if(boxSizing === 'content-box') {
        height = height - paddingSize;
    }
}

注意HIDDEN_STYLE中,height: 0;這個很是重要。由於不這麼作的話,你取到的scrollHeight的值是組件輸入框的高度,而不是他輸入值文本所佔的高度。rem

還有一點須要提下,仔細看HIDDEN_STYLE的屬性,咱們都是經過visible, top, right, opacity來隱藏這個節點,而沒有使用display:none; 由於,設置dispaly:none後,就沒有這個節點了, scrollHeight是獲取不到值的!!!get

總結

只要明白了實現的思路,其實後面就好寫了。關鍵點仍是在於對於box-sizing,以及模擬樣式的健壯性考慮。

經過這個組件:

  • 加深了對於scrollHeight的使用
  • 對於代碼健壯性的理解,須要考慮多種狀況(box-sizing)
相關文章
相關標籤/搜索