一. 自适应高度功能的介绍
1.1 自适应高度功能描述
自适应高度功能是指文本域跟随字数的增加或减少,从而动态的调整文本域的高度。
1.2 自适应高度功能相关属性
在 element-ui 的 Input 组件中,设置了 type="textarea"
,再设置 autosize
属性,可以开启自适应文本高度功能。
序号 | 参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|---|
1 | autosize | 自适应内容高度,只对 type=textarea 有效,可传入对象,如,{ minRows: 2, maxRows: 6 } | boolean / object | -- | false |
二. 自适应高度功能的具体实现
2.1 自适应高度功能的实现思路
创建一个隐藏的 textarea,放到 body
标签下,将 Textarea 组件的 value
值赋值给隐藏的 textarea,通过获取这个隐藏的 textarea 的 scrollHeight
(scrollHeight
会返回该元素在不使用滚动条时的高度),来设置组件上面的 textarea 的高度。
2.2 计算自适应高度的方法
在 element-ui 源代码中,calcTextareaHeight.js
文件的 calcTextareaHeight
方法用于计算 textarea 的动态高度。
计算的步骤分析如下:
- 首先获取
Input type="textarea"
组件的与元素大小有关全部 CSS 属性,将 textarea 的box-sizing
,上下边距的padding
,上下两边的border
分别解构出来。 - 创建一个 textarea 元素,将刚刚获取到的组件全部的样式,以及隐藏元素的样式全部给创建的 textarea。
- 获取隐藏元素的
scrollHeight
,返回的值是该元素在不使用滚动条的时候的content
高度和padding
整体高度,不包含border
的高度。 - 判断是标准盒子模型还是IE盒子模型,标准盒子模型的高度需要减去
padding
的上下边距总和,IE盒子模型的高度需要加上上下的border
的总和。 - 将
value
值设置为空,计算出一行的高度,用scrollHeight
减去上下两边的内边距,就是一行文字内容的高度。 - 如果传入了最小行数
minRows
,则根据传入的最小行数计算出 textarea 的最小高度:
(1)如果是IE盒子模型,则最小高度需要加上padding
和border
。
(2)取隐藏 textarea的高度和最小高度中较大的作为组件的最小高度minHeight
(取较大的是因为不能小于最小高度)。 - 如果传入了最大行数
maxRows
,则根据传入的最大行数计算出 textarea 的最大高度:
(1)如果是IE盒子模型,则最小高度需要加上padding
和border
。
(2)取隐藏 textareas的高度和最大高度中较小的作为组件的高度height
(取较小的是因为不能大于最大高度)。
具体代码如下:
js
// 设置变量 hiddenTextarea 去存放隐藏的元素
let hiddenTextarea;
// 隐藏元素的 style
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
`;
// 影响 textarea 高度的全部相关的 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'
];
// 获取目标元素的 box-sizing、padding、border、以及其他与元素大小有关的全部属性
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'))
);
// 首先获取传入的 textarea 的与元素大小有关全部CSS属性
const contextStyle = CONTEXT_STYLE
.map(name => `${name}:${style.getPropertyValue(name)}`)
.join(';');
return { contextStyle, paddingSize, borderSize, boxSizing };
}
// 计算 textarea 自适应高度的方法
export default function calcTextareaHeight(
targetElement,
minRows = 1,
maxRows = null
) {
// 设置一个隐藏的 textarea 用于获取其 scrollHeight
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
let {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(targetElement);
hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';
// scrollHeight 返回该元素在不使用滚动条时的高度,包含元素内容content的高度,以及内边距padding的高度,但是
let height = hiddenTextarea.scrollHeight;
const result = {};
if (boxSizing === 'border-box') {
// 如果是『IE盒子模型』则高度需要加上边框的高度,因为IE盒子模型的高度是 content+padding+border
height = height + borderSize;
} else if (boxSizing === 'content-box') {
// 如果是『标准盒子模型』则高度需要减去 padding 的高度,因为标准盒子模型的高度只有 content 的高度
height = height - paddingSize;
}
// 将value设置为空,计算出1行的高度
hiddenTextarea.value = '';
let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
// 如果传入了最小行数 minRows
if (minRows !== null) {
// 根据传入的最小行数计算出最小高度
let minHeight = singleRowHeight * minRows;
// 如果是IE盒子模型,则最小高度需要加上 padding 和 border
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
// 取隐藏textarea和最小高度中较大的作为组件的最小高度 minHeight
// 这样 textarea 在小于最小高度时取最小高度,在大于最小高度时取自适应高度
height = Math.max(minHeight, height);
result.minHeight = `${ minHeight }px`;
}
// 如果传入了最大行数 maxRows
if (maxRows !== null) {
// 则根据传入的最大行数计算出最大高度
let maxHeight = singleRowHeight * maxRows;
// 如果是IE盒子模型,则最大高度需要加上 padding 和 border
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
// 取隐藏textarea和最大高度中较小的作为组件的高度 height
// 这样超过最大高度时出现滚动条,小于最大高度时自适应
height = Math.min(maxHeight, height);
}
result.height = `${ height }px`;
hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
hiddenTextarea = null;
return result;
};
2.3 计算自适应高度方法的调用时机
mounted
钩子里要进行计算。- 监听
value
值改变时需要去计算。 - Input 组件的
type
发生变化时需要去计算。