如何自动调整文本区域的大小以适应内容?

How to auto resize the textarea to fit the content?

我正在尝试自动调整 textarea 的大小以使其适合其中的内容,但在我单击 enter 继续下一行后我遇到了一个卡顿问题。我该如何解决这个问题?

这就是我想要做的,请参见下图。

有关 StackBlitz 示例,请参阅此 link

代码

this.form.valueChanges.subscribe(() => {
   const textarea = this.myDiv.nativeElement;
   textarea.addEventListener('keydown', function() {
       setTimeout(() => {
           this.style.cssText = 'height:auto; padding:0';
           this.style.cssText = 'height:' + this.scrollHeight + 'px';
       }, 0);
   });
});

你想要实现的是一个非常古老的把戏。我自己使用过它,但尝试了不同的方法。

为什么文本区域会跳动更有意义,因为您在每次击键时 height = 0 计算滚动高度,以便您可以分配新的高度。

我计算了 fontSize 或 lineHeight 并计算了行数和初始高度以根据它进行调整。所以在每次击键时你只是分配高度 w/o 使文本区域高度=0

textareaProps = null;
getHeight(element) {
  const lines = element.value.split(/\r\n|\r|\n/).length;
  if(!this.textareaProps) {
    const autoStyle = getComputedStyle(element);
    const lineHeight = parseInt(autoStyle.lineHeight);
    const adjust = parseInt(autoStyle.height) - lineHeight;
    this.textareaProps = {adjust, lineHeight}
  }
  const { adjust, lineHeight } = this.textareaProps;
  const height = lines * lineHeight + adjust;
  return height + 'px';
}

您现在需要调用此方法来获取高度并将 textarea 元素作为 arg 传递。

element.style.cssText = 'height:' + getHeight(element) ;

编辑 2

遗憾的是,上述解决方案仅在用户换行时才有效。当您输入大行时,文本区域会将其包裹起来,但不会增加高度。因此引入一个代理 html 元素,该元素的文本与文本区域值相同,并将提供我们可以分配给文本区域的高度。

textareaProps = null;
getHeight(element) {
  if(!this.textareaProps) {
    const proxy = document.createElement('div');
    const {padding, width, fontSize, height, lineHeight} = getComputedStyle(element);
    const css = [
      'position:absolute',
      'visibility: hidden',
      'pointer-events:none',
      `width: ${width}`,
      `padding:${padding}`,
      `min-height: ${height}`,
      `font-size:${fontSize}`,
      `line-height:${lineHeight}`,
    ].join(';');
    proxy.style.cssText=css;
    this.textareaProps = {
      proxy: document.body.appendChild(proxy), 
      adjust: (parseInt(fontSize))
    };
  }
  const { proxy, adjust} = this.textareaProps;
  proxy.innerText = element.value + '.';
  return (proxy.offsetHeight + adjust) + 'px';
}

已更新 StackBlitz https://stackblitz.com/edit/next-line-view-child-ssnp4q

addEventListener 这里是多余的,因为 valueChanges 已经在字段更改时通知您。相反,使用 ViewChild 参考 myDiv.

更新高度
this.myForm.valueChanges.subscribe(value => {
    this.myDiv.nativeElement.style.height = 'auto';
    this.myDiv.nativeElement.style.height = `${this.myDiv.nativeElement.scrollHeight}px`;
});

然后将 overflow: hidden 添加到 css,这样滚动条就不会显示了。

textarea {
    resize: horizontal;
    overflow: hidden;
}

您可以保留 resize: horizontal;,但不再需要它,因为文本区域无论如何都会自动调整大小。

这是 StackBlitz 上的一个工作示例。

对于将近 2021 年仍在寻找此问题答案的任何人,它已涵盖 in the official Angular Material docs here。通过 nativeElement 直接操纵 DOM 是一种反模式。

<mat-form-field [style.fontSize]="fontSize.value">
  <mat-label>Autosize textarea</mat-label>
  <textarea matInput
            cdkTextareaAutosize
            #autosize="cdkTextareaAutosize"
            cdkAutosizeMinRows="1"
            cdkAutosizeMaxRows="5"></textarea>
</mat-form-field>