1. 程式人生 > >關於Element的textarea自適應高度

關於Element的textarea自適應高度

今天觀看學習Element的原始碼,看到textarea有一個自適應高度的屬性,畢竟以前也接觸過這方面的問題,你可以檢視此處:更強大的textarea高度自適應來了解我之前寫的一篇同樣是實現textarea自適應高度,所以就好奇看一下它是怎麼實現的。

先來看一下它的原始碼吧(各個階段大致的做的事情我已經標到程式碼上了):

let hiddenTextarea;

const HIDDEN_STYLE = `
  height:0 !important;
  visibility:hidden !important;
  overflow:hidden !important;
  position:absolute !important;
  z-index:-1000
!important; top:0 !important; right:0 !important `; // 所有可能會影響到height的css屬性 const CONTEXT_STYLE = [ 'letter-spacing', 'line-height', 'padding-top', 'padding-bottom', 'font-family', 'font-weight', 'font-size', 'text-rendering', 'text-transform', 'width', 'text-indent', 'padding-left'
, 'padding-right', 'border-width', 'box-sizing' ]; // 獲取設定在當前textarea上的css屬性 function calculateNodeStyling(targetElement) { const style = window.getComputedStyle(targetElement); const boxSizing = style.getPropertyValue('box-sizing'); const paddingSize = ( parseFloat(style.getPropertyValue('padding-bottom'
)) + parseFloat(style.getPropertyValue('padding-top')) ); const borderSize = ( parseFloat(style.getPropertyValue('border-bottom-width')) + parseFloat(style.getPropertyValue('border-top-width')) ); const contextStyle = CONTEXT_STYLE .map(name => `${name}:${style.getPropertyValue(name)}`) .join(';'); return { contextStyle, paddingSize, borderSize, boxSizing }; } export default function calcTextareaHeight( targetElement, minRows = 1, maxRows = null ) { // 如果不存在就新建一個隱藏的textarea if (!hiddenTextarea) { hiddenTextarea = document.createElement('textarea'); document.body.appendChild(hiddenTextarea); } let { paddingSize, borderSize, boxSizing, contextStyle } = calculateNodeStyling(targetElement); // 將獲取到得當前得textarea的css屬性作用於隱藏的textarea hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`); // 將當前的textarea的value設定到隱藏的textarea上面 hiddenTextarea.value = targetElement.value || targetElement.placeholder || ''; // 獲取隱藏的textarea的height 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); } // 將得到的height的高度設定到當前的textarea上面 result.height = `${ height }px`; // 刪除掉無用的隱藏的textarea hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea); hiddenTextarea = null; return result; };

大致思路就是將當前textarea的所有可能會影響到height的css屬性全部設定給一個隱藏的textarea上,並且兩個的value一樣,再將隱藏的textarea的高度設定給當前的textarea,如果設定有最小和最大行數,則再做相應的事件。

不過這個是Element的一個檔案,我們一般情況下也沒辦法使用,所以我就想辦法把它修改為了一個依賴與jQuery的js檔案,使用的時候只需要直接引用這個js檔案就可以了,其他事件都不需要做,並且也支援設定最小和最大行數的。

下面放上一個例子:
html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>textarea自適應高度</title>
    <style>
        textarea {
            display: block;
            resize: vertical;
            padding: 5px 15px;
            line-height: 1.5;
            box-sizing: border-box;
            width: 100%;
            font-size: 14px;
            color: #5a5e66;
            background-color: #fff;
            background-image: none;
            border: 1px solid #d8dce5;
            border-radius: 4px;
            transition: border-color .2s cubic-bezier(.645,.045,.355,1);
            margin-bottom: 20px;
        }
    </style>
</head>
<body>
    <textarea name="text" cols="30" rows="1"></textarea>
    <textarea name="text" cols="30" rows="2"></textarea>
    <textarea name="text" cols="30" data-min-rows="2" data-max-rows="4"></textarea>
    <script src="http://apps.bdimg.com/libs/jquery/1.11.3/jquery.js"></script>
    <script src="autoheight-textarea.js"></script>
</body>
</html>

autoheight-textarea.js檔案:

$(function() {
    var  hiddenTextarea;

    var HIDDEN_STYLE = `
        height:0 !important;
        visibility:hidden !important;
        overflow:hidden !important;
        position:absolute !important;
        z-index:-1000 !important;
        top:0 !important;
        right:0 !important;
    `;

    // 所有可能會影響到height的css屬性
    var CONTEXT_STYLE = [
        'letter-spacing',
        'line-height',
        'padding-top',
        'padding-bottom',
        'font-family',
        'font-weight',
        'font-size',
        'text-rendering',
        'text-transform',
        'width',
        'text-indent',
        'padding-left',
        'padding-right',
        'border-width',
        'box-sizing'
    ];
    // 獲取設定在當前textarea上的css屬性
    function calculateNodeStyling(targetElement) {
        var style = window.getComputedStyle(targetElement);

        var boxSizing = style.getPropertyValue('box-sizing');

        var paddingSize = (
            parseFloat(style.getPropertyValue('padding-bottom')) +
            parseFloat(style.getPropertyValue('padding-top'))
        );

        var borderSize = (
            parseFloat(style.getPropertyValue('border-bottom-width')) +
            parseFloat(style.getPropertyValue('border-top-width'))
        );

        var contextStyle = CONTEXT_STYLE
            .map(function(value){
                return value + ':' + style.getPropertyValue(value)
            }).join(';');

        return { contextStyle, paddingSize, borderSize, boxSizing };
    }
    $('body').on('focus', 'textarea', function () {
        // 如果不存在就新建一個隱藏的textarea
        var _this = $(this);
        if (!hiddenTextarea) {
            hiddenTextarea = document.createElement('textarea');
            document.body.appendChild(hiddenTextarea);
        }
        var {
            paddingSize,
            borderSize,
            boxSizing,
            contextStyle
        } = calculateNodeStyling(_this[0]);
        // 將獲取到得當前得textarea的css屬性作用於隱藏的textarea
        hiddenTextarea.setAttribute('style', HIDDEN_STYLE + contextStyle);
    }).on('keydown keyup', 'textarea', function(){
        var _this = $(this);
        var {
            paddingSize,
            borderSize,
            boxSizing,
            contextStyle
        } = calculateNodeStyling(_this[0]);
        // 將獲取到得當前得textarea的css屬性作用於隱藏的textarea
        hiddenTextarea.setAttribute('style', HIDDEN_STYLE + contextStyle);
        // 將當前的textarea的value設定到隱藏的textarea上面
        hiddenTextarea.value = _this[0].value || _this[0].placeholder || '';

        // 獲取隱藏的textarea的height
        var height = hiddenTextarea.scrollHeight;

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

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

        // 如果設定有最小行數和最大行數時的判斷條件,如果沒有設定則取rows為最小行數
        var minRows = _this.attr('rows') ? _this.attr('rows') :  _this.attr('data-min-rows') ? _this.attr('data-min-rows') : 1;
        var maxRows = _this.attr('data-max-rows') ? _this.attr('data-max-rows') : null;
        if (minRows !== null) {
            var minHeight = singleRowHeight * minRows;
            if (boxSizing === 'border-box') {
                minHeight = minHeight + paddingSize + borderSize;
            }
            height = Math.max(minHeight, height);
        }
        if (maxRows !== null) {
            var maxHeight = singleRowHeight * maxRows;
            if (boxSizing === 'border-box') {
                maxHeight = maxHeight + paddingSize + borderSize;
            }
            height = Math.min(maxHeight, height);
        }

        // 將得到的height的高度設定到當前的textarea上面
        _this.css('height', height + 'px');
    }).on('blur', 'textarea', function () {
        // 刪除掉無用的隱藏的textarea
        hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
        hiddenTextarea = null;
    })
})

Element的原始碼還是寫的很讚的,有時間多看看各種框架的原始碼。