今天观看学习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:
<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的源码还是写的很赞的,有时间多看看各种框架的源码。