Vue开发:分享textarea自适应高度方案。
常见得方案有两种,一种是在页面地“边远地区”添加一个ghost dom来模拟输入换行,这个dom的可能是editable属性为true的div或者是一个一摸一样得textarea。
以element-ui的input组件举例,当我们在组件内输入值时,会调用resizeTextarea方法。
resizeTextarea() { if (this.$isServer) return; const { autosize, type } = this; if (type !== 'textarea') return; if (!autosize) { this.textareaCalcStyle = { minHeight: calcTextareaHeight(this.$refs.textarea).minHeight }; return; } const minRows = autosize.minRows; const maxRows = autosize.maxRows; this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows); }
当设置了autosize为true则textarea设为自适应高度。此时textarea的高度会通过calcTextareaHeight方法实时计算。
export default function calcTextareaHeight( targetElement, minRows = 1, maxRows = null ) { 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 || ''; 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); } result.height = `${ height }px`; hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea); hiddenTextarea = null; return result; };
我们可以看到:
if (!hiddenTextarea) { hiddenTextarea = document.createElement('textarea'); document.body.appendChild(hiddenTextarea); }
element-ui创建了一个textarea的dom,通过calculateNodeStyling方法将真正的textarea的样式复制给hiddenTextarea(overflow不同步,真正的textarea是为hidden)。接着监听textarea的输入值,同步给hiddenTextarea。同时将hiddenTextarea的scrollHeight同步给textarea的高度,最后再将dom销毁掉。
关于样式的同步,element这里用了getComputedStyle和getPropertyValue这两个API。当然,如果你自己封装的话,也可以使用css预处理器的mixin。
第二种方案与第一种方案类似,不过不会创建额外的dom。以开头的vue-awesome-textarea举例:
init() { this.initAutoResize() }, initAutoResize () { this.autoResize && this.$nextTick(this.calcResize) }
在页面mounted或者内容变动且开启自适应高度autoResize的时候,执行this.calcResize方法。
calcResize() { this.resetHeight() this.calcTextareaH() }, resetHeight() { this.height = 'auto' }, calcTextareaH() { let contentHeight = this.calcContentHeight() this.height = this.calcHeightChange(contentHeight) + 'px' if (this.needUpdateRows(contentHeight)) { this.updateRows(contentHeight) } this.oldContentHeight = contentHeight }, calcContentHeight () { const { paddingSize } = this.calcNodeStyle(this.$el) return this.$el.scrollHeight - paddingSize },
resetHeight()是来初始化textarea的高度,默认为auto。calcTextareaH()方法是用来计算内容区域的高度(textarea的scrollHeight减去padding的高度),同时将计算好的高度实时同步给textarea的高:
this.height = this.calcHeightChange(contentHeight) + 'px'
相比方案一,这个方案采用的思路相同(动态修改高度),但是减少了额外的dom创建和销毁的过程。
此外,vue-awesome-textarea还支持在自适应的过程中回调行数,可以更好的支持数据回显。实现的方法也很简单:
computed: { ... oneRowsHeight() { return this.calcContentHeight() / Number(this.rows) || 0 } ... }
在computed中我们计算出单行的高度,同时在执行this.calcTextareaH()方法时我们记录下内容高度:
this.oldContentHeight = contentHeight
接着我们会比对是否存在添加行操作,一旦添加则新的内容高度和老的内容高度会不同:
needUpdateRows(newContentHeight) { return this.oldContentHeight !== newContentHeight },
此时我们会把最新的行高emit到组件外部:
updateRows(contentHeight) { this.$emit('getRows', Math.round(contentHeight / this.oneRowsHeight)) }
网友评论文明上网理性发言 已有0人参与
发表评论: