可适应文本高度的输入框组件其实一开始是Ant design里面就有的,Element-ui也实现了,但是通过看源码,其实用的都是一个相似的js...
我们需要实现的这个组件,可以指定最小行数(minRows
)和最大行数(maxRows
),用来限定textarea的高度变化范围。
最终实现的效果
说一下具体的实现思路吧
- 在body中添加一个不可见的元素,样式尽可能的和组件输入框的一样。当输入框的值发生变化时,通过这个不可见的元素,去模拟计算这时候组件输入框文本所占高度height。
- 通过计算出单行高度,获得
minRows * singleRowHeight
与maxRows *singleRowHeight
,这样就有了一个最大、最小高度范围。 - 最后,通过这三个值:
height, minHeight, maxHeight
.做一下范围判断,就可以获得组件输入框的实时高度了。
具体代码逻辑:
// 不可见的节点,用来模拟输入框,计算当前输入值所占的高度
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.scrollHeight
从下图中,我们可以很直观的看出,scrollHeight = 内容区域高度(content-height) + paddingSize
。不包括border以及margin的值。
在这个组件中,我们需要去模拟当前输入框的样式,去计算当前输入值会占多少的高度,就需要用到这个属性。
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的值是组件输入框的高度,而不是他输入值文本所占的高度。
还有一点需要提下,仔细看HIDDEN_STYLE
的属性,我们都是通过visible, top, right, opacity来隐藏这个节点,而没有使用display:none; 因为,设置dispaly:none后,就没有这个节点了, scrollHeight是获取不到值的!!!
总结
只要明白了实现的思路,其实后面就好写了。关键点还是在于对于box-sizing,以及模拟样式的健壮性考虑。
通过这个组件:
- 加深了对于scrollHeight的使用
- 对于代码健壮性的理解,需要考虑多种情况(box-sizing)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。