如何自动调整文本区域的大小以适应内容?
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>
我正在尝试自动调整 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>