设为首页收藏本站

安徽论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10486|回复: 0

Vue中textarea自适应高度方案的实现

[复制链接]

76

主题

0

回帖

240

积分

中级会员

Rank: 3Rank: 3

积分
240
发表于 2022-3-26 10:59:27 | 显示全部楼层 |阅读模式
网站内容均来自网络,本站只提供信息平台,如有侵权请联系删除,谢谢!
目录

先给方案,Vue栈有需求的同学可以直接下载vue-awesome-textarea

隐藏的问题

抛开原生JS,框架的大部分UI库都支持自适应textarea高度功能,但普遍都忽略了一个功能,就是自适应高度的回显。
使用这些库的时候,我们很容易的在textarea中键入内容,超出范围时会自动延展一行,保证内容高度的自适应。当我们提交内容,在其它页面使用同样的UI来渲染时,麻烦的就来了,有些UI库是不支持自适应回显的,这就需要我们通过行高、行数甚至高度之间的计算得出一个基值,从而实现回显。

解决自适应高度的方案

常见得方案有两种,一种是在页面地“边远地区”添加一个ghost dom来模拟输入换行,这个dom的可能是editable属性为true的div或者是一个一摸一样得textarea。
以element-ui的input组件举例,当我们在组件内输入值时,会调用resizeTextarea方法
  1. resizeTextarea() {
  2.   if (this.$isServer) return;
  3.   const { autosize, type } = this;
  4.   if (type !== 'textarea') return;
  5.   if (!autosize) {
  6.     this.textareaCalcStyle = {
  7.       minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
  8.     };
  9.     return;
  10.   }
  11.   const minRows = autosize.minRows;
  12.   const maxRows = autosize.maxRows;

  13.   this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
  14. }
复制代码
当设置了autosize为true则textarea设为自适应高度。此时textarea的高度会通过calcTextareaHeight方法实时计算。
  1. export default function calcTextareaHeight(
  2.   targetElement,
  3.   minRows = 1,
  4.   maxRows = null
  5. ) {
  6.   if (!hiddenTextarea) {
  7.     hiddenTextarea = document.createElement('textarea');
  8.     document.body.appendChild(hiddenTextarea);
  9.   }

  10.   let {
  11.     paddingSize,
  12.     borderSize,
  13.     boxSizing,
  14.     contextStyle
  15.   } = calculateNodeStyling(targetElement);

  16.   hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
  17.   hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';

  18.   let height = hiddenTextarea.scrollHeight;
  19.   const result = {};

  20.   if (boxSizing === 'border-box') {
  21.     height = height + borderSize;
  22.   } else if (boxSizing === 'content-box') {
  23.     height = height - paddingSize;
  24.   }

  25.   hiddenTextarea.value = '';
  26.   let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

  27.   if (minRows !== null) {
  28.     let minHeight = singleRowHeight * minRows;
  29.     if (boxSizing === 'border-box') {
  30.       minHeight = minHeight + paddingSize + borderSize;
  31.     }
  32.     height = Math.max(minHeight, height);
  33.     result.minHeight = `${ minHeight }px`;
  34.   }
  35.   if (maxRows !== null) {
  36.     let maxHeight = singleRowHeight * maxRows;
  37.     if (boxSizing === 'border-box') {
  38.       maxHeight = maxHeight + paddingSize + borderSize;
  39.     }
  40.     height = Math.min(maxHeight, height);
  41.   }
  42.   result.height = `${ height }px`;
  43.   hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
  44.   hiddenTextarea = null;
  45.   return result;
  46. };
复制代码
我们可以看到
  1. if (!hiddenTextarea) {
  2.   hiddenTextarea = document.createElement('textarea');
  3.   document.body.appendChild(hiddenTextarea);
  4. }
复制代码
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举例:
  1. init() {
  2.   this.initAutoResize()
  3. },
  4. initAutoResize () {
  5.   this.autoResize && this.$nextTick(this.calcResize)
  6. }
复制代码
在页面mounted或者内容变动且开启自适应高度autoResize的时候,执行this.calcResize方法。
  1. calcResize() {
  2.   this.resetHeight()
  3.   this.calcTextareaH()
  4. },

  5. resetHeight() {
  6.   this.height = 'auto'
  7. },

  8. calcTextareaH() {
  9.   let contentHeight = this.calcContentHeight()
  10.   this.height = this.calcHeightChange(contentHeight) + 'px'
  11.   if (this.needUpdateRows(contentHeight)) {
  12.     this.updateRows(contentHeight)
  13.   }
  14.   this.oldContentHeight = contentHeight
  15. },

  16. calcContentHeight () {
  17.   const { paddingSize } = this.calcNodeStyle(this.$el)
  18.   return this.$el.scrollHeight - paddingSize
  19. },
复制代码
resetHeight()是来初始化textarea的高度,默认为auto。calcTextareaH()方法是用来计算内容区域的高度(textarea的scrollHeight减去padding的高度),同时将计算好的高度实时同步给textarea的高:
this.height = this.calcHeightChange(contentHeight) + 'px'
相比方案一,这个方案采用的思路相同(动态修改高度),但是减少了额外的dom创建和销毁的过程。
此外,vue-awesome-textarea还支持在自适应的过程中回调行数,可以更好的支持数据回显。实现的方法也很简单:
  1. computed: {
  2.   ...
  3.   oneRowsHeight() {
  4.     return this.calcContentHeight() / Number(this.rows) || 0
  5.   }
  6.   ...
  7. }
复制代码
在computed中我们计算出单行的高度,同时在执行this.calcTextareaH()方法时我们记录下内容高度:
  1. this.oldContentHeight = contentHeight
复制代码
接着我们会比对是否存在添加行操作,一旦添加则新的内容高度和老的内容高度会不同:
  1. needUpdateRows(newContentHeight) {
  2.   return this.oldContentHeight !== newContentHeight
  3. },
复制代码
此时我们会把最新的行高emit到组件外部:
  1. updateRows(contentHeight) {
  2.   this.$emit('getRows', Math.round(contentHeight / this.oneRowsHeight))
  3. }
复制代码
到此这篇关于Vue中textarea自适应高度方案的实现的文章就介绍到这了,更多相关Vue textarea自适应内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
                                                        
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
免责声明
1. 本论坛所提供的信息均来自网络,本网站只提供平台服务,所有账号发表的言论与本网站无关。
2. 其他单位或个人在使用、转载或引用本文时,必须事先获得该帖子作者和本人的同意。
3. 本帖部分内容转载自其他媒体,但并不代表本人赞同其观点和对其真实性负责。
4. 如有侵权,请立即联系,本网站将及时删除相关内容。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表