可自适应文本高度的输入框

可适应文本高度的输入框组件其实一开始是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)
相关文章
相关标签/搜索