关于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,如果设置有最小和最大行数,则再做相应的事件。

如果您不太清楚getComputedStyle和getPropertyValue,可以直接查看此处http://www.zhangxinxu.com/wordpress/2012/05/getcomputedstyle-js-getpropertyvalue-currentstyle/

不过这个是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的源码还是写的很赞的,有时间多看看各种框架的源码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值