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

时间:2019-09-24 04:16:51

标签: javascript html css angular textarea

我正在尝试自动调整textarea的大小,以使其适合其中的内容,但是单击Enter进入下一行后,我遇到了一个口吃的问题。我该如何解决?

这是我要尝试的操作,请参见下图。 enter image description here

有关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);
   });
});

3 个答案:

答案 0 :(得分:2)

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上的一个有效示例。

答案 1 :(得分:1)

您要实现的目标是一个很老的技巧。我自己使用过,但尝试了另一种方法。

您每次在height = 0进行每次击键来计算滚动高度时,为什么文本区域都是跳动的,所以您可以分配一个新的高度。

我计算了fontSize或lineHeight,并计算了行数和基于该行数调整的初始高度。因此,在每次击键时,您只是分配了高度,而没有使文本区域的高度为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元素,该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

答案 2 :(得分:0)

对于在2021年左右仍在寻找答案的任何人,其内容已涵盖in the official Angular Material docs here。通过nativeElement直接操作DOM是一种反模式。

  <mat-label>Autosize textarea</mat-label>
  <textarea matInput
            cdkTextareaAutosize
            #autosize="cdkTextareaAutosize"
            cdkAutosizeMinRows="1"
            cdkAutosizeMaxRows="5"></textarea>
</mat-form-field>

TS:

import {CdkTextareaAutosize} from '@angular/cdk/text-field';
import {Component, NgZone, ViewChild} from '@angular/core';
import {take} from 'rxjs/operators';

/** @title Auto-resizing textarea */
@Component({
  selector: 'text-field-autosize-textarea-example',
  templateUrl: './text-field-autosize-textarea-example.html',
  styleUrls: ['./text-field-autosize-textarea-example.css'],
})
export class TextFieldAutosizeTextareaExample {
  constructor(private _ngZone: NgZone) {}

  @ViewChild('autosize') autosize: CdkTextareaAutosize;

  triggerResize() {
    // Wait for changes to be applied, then trigger textarea resize.
    this._ngZone.onStable.pipe(take(1))
        .subscribe(() => this.autosize.resizeToFitContent(true));
  }
}