🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
![](https://box.kancloud.cn/6651c43748df4921aaec6ff2a2318ab3_695x285.png) 如图,微博的输入框是高度自适应的,随着内容的增多,高度越来越高。 这个技术的关键点在于,**如何计算内容所撑开的高度,然后动态的设置textarea的height.** 其次,如果根据内容动态设置height, 那当内容只有一行的时候,也给textarea设置计算的height吗?肯定不行啊,所以要考虑到**最小高度的设置**。 最终结果[calcTextareaHeight.js](https://github.com/jvsheng/utils/blob/master/From-element/calcTextareaHeight.js) **整体思路** 1. 我们通过新增一个textarea节点, 让它的样式和内容和需要计算高度的那个textarea基本没差. 2. 为它加一个hidden样式,不能因为新增节点,导致布局发生改变。 3. 通过scrollHeight获取高度,考虑box-sizing属性会造成一些变异,需要计算一下真实需要返回的height值。 4. 考虑最小高度和最大高度,我们通过minRows和maxRows所以,需要考虑计算单行文本的高度。 **DOM优化** 如果去试了微博的输入框,会发现,当回车的时候,显示上去了一点,然后才取消滚动条撑开textarea,显得不是非常的流畅。 Vue的$nextTick就刚好可以解决这个问题。在组件中,监听input事件,**如果值value没有改变,直接返回,不要做过多的计算**。否则,在$nextTick中,计算动态高度。这样,在textarea的value发生了数据改变后,会在下次DOM渲染前计算好高度,这样就会显得非常流畅了。 **获取文本高度** 我们要完成的是一个,支持最小高度和最大高度的textarea组件,所以,通过获取height属性来获取高度肯定是不行的,需要用到[scrollHeight](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollHeight)。scrollHeight返回的是content + padding的高度。 组件兼容性的考虑,要想到box-sizing这种情况,返回真实的height值。 如果为border-box, 返回的应该是 scrollHeight + border高度。 如果为content-box, 返回的应该是 scrollHeight - padding高度。 所有高度判断的代码都写在一堆,中间还夹杂了style样式的计算?no!!! 看下大神对于函数的拆分 ```js function calcNodeStyling(node) { var style = window.getComputedStyle(node); var boxSizing = style.getPropertyValue('box-sizing'); var paddingSize = ( parseInt(style.getPropertyValue('padding-top')) + parseInt(style.getPropertyValue('padding-bottom')) ); var borderSize = ( parseInt(style.getPropertyValue('border-top')) + parseInt(style.getPropertyValue('border-bottom')) ); var contextStyle = CONTEXT_STYLE.map(function(name) { return name + ':' + style.getPropertyValue('name'); }).join(';'); return { 'boxSizing': boxSizing, 'paddingSize': paddingSize, 'borderSize': borderSize, 'contextStyle': contextStyle }; } ``` **最小高度与最大高度** ```js function calcTextareaHeight(node, minRows, maxRows) { //````````````````` hiddenTextarea.value = ''; var singleRowHieght = hiddenTextarea.scrollHeight - calStyle.paddingSize; if (minRows) { var minHeight = singleRowHeight * minRows; if (calStyle.boxSizing === 'border-box') { minHeight = minHeight + calStyle.paddingSize + calStyle.borderSize; } height = Math.max(minHeight, height); } if (maxRows) { var maxHeight = singleRowHeight * maxRows; if (calStyle.boxSizing === 'border-box') { maxHeight = maxHeight + calStyle.paddingSize + calStyle.borderSize; } height = Math.min(maxHeight, height); } return { height: height + 'px' }; } ``` **代码的优化** 1. 对于hidden样式和原有样式,放在了一个数组中,通过map函数和join函数,转换为字符串,不像for循环那样,显得臃肿。 2. 对于计算样式方面,抽离函数出来,然后返回一个对象,将需要返回的信息都放在里面。整个js看起来也显得更简洁了。 3. 写组件要考虑适用性,比如这里如果不考虑box-sizing, 问题就非常严重,有些地方根本没法用。